1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.launcher3;
18
19 import android.app.SearchManager;
20 import android.appwidget.AppWidgetManager;
21 import android.appwidget.AppWidgetProviderInfo;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.ContentProviderClient;
25 import android.content.ContentProviderOperation;
26 import android.content.ContentResolver;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.Intent.ShortcutIconResource;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ProviderInfo;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Configuration;
37 import android.content.res.Resources;
38 import android.database.Cursor;
39 import android.graphics.Bitmap;
40 import android.graphics.BitmapFactory;
41 import android.net.Uri;
42 import android.os.Environment;
43 import android.os.Handler;
44 import android.os.HandlerThread;
45 import android.os.Parcelable;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.SystemClock;
49 import android.provider.BaseColumns;
50 import android.text.TextUtils;
51 import android.util.Log;
52 import android.util.Pair;
53
54 import com.android.launcher3.compat.AppWidgetManagerCompat;
55 import com.android.launcher3.compat.LauncherActivityInfoCompat;
56 import com.android.launcher3.compat.LauncherAppsCompat;
57 import com.android.launcher3.compat.PackageInstallerCompat;
58 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
59 import com.android.launcher3.compat.UserHandleCompat;
60 import com.android.launcher3.compat.UserManagerCompat;
61
62 import java.lang.ref.WeakReference;
63 import java.net.URISyntaxException;
64 import java.security.InvalidParameterException;
65 import java.text.Collator;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.Collection;
69 import java.util.Collections;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Iterator;
74 import java.util.List;
75 import java.util.Map.Entry;
76 import java.util.Set;
77 import java.util.TreeMap;
78 import java.util.concurrent.atomic.AtomicBoolean;
79
80 /**
81 * Maintains in-memory state of the Launcher. It is expected that there should be only one
82 * LauncherModel object held in a static. Also provide APIs for updating the database state
83 * for the Launcher.
84 */
85 public class LauncherModel extends BroadcastReceiver
86 implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
87 static final boolean DEBUG_LOADERS = false;
88 private static final boolean DEBUG_RECEIVER = false;
89 private static final boolean REMOVE_UNRESTORED_ICONS = true;
90
91 static final String TAG = "Launcher.Model";
92
93 // true = use a "More Apps" folder for non-workspace apps on upgrade
94 // false = strew non-workspace apps across the workspace on upgrade
95 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
96 public static final int LOADER_FLAG_NONE = 0;
97 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
98 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
99
100 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
101 private static final long INVALID_SCREEN_ID = -1L;
102
103 private final boolean mAppsCanBeOnRemoveableStorage;
104 private final boolean mOldContentProviderExists;
105
106 private final LauncherAppState mApp;
107 private final Object mLock = new Object();
108 private DeferredHandler mHandler = new DeferredHandler();
109 private LoaderTask mLoaderTask;
110 private boolean mIsLoaderTaskRunning;
111 private volatile boolean mFlushingWorkerThread;
112
113 // Specific runnable types that are run on the main thread deferred handler, this allows us to
114 // clear all queued binding runnables when the Launcher activity is destroyed.
115 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
116 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
117
118 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
119
120 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
121 static {
122 sWorkerThread.start();
123 }
124 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
125
126 // We start off with everything not loaded. After that, we assume that
127 // our monitoring of the package manager provides all updates and we never
128 // need to do a requery. These are only ever touched from the loader thread.
129 private boolean mWorkspaceLoaded;
130 private boolean mAllAppsLoaded;
131
132 // When we are loading pages synchronously, we can't just post the binding of items on the side
133 // pages as this delays the rotation process. Instead, we wait for a callback from the first
134 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
135 // a normal load, we also clear this set of Runnables.
136 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
137
138 private WeakReference<Callbacks> mCallbacks;
139
140 // < only access in worker thread >
141 AllAppsList mBgAllAppsList;
142
143 // The lock that must be acquired before referencing any static bg data structures. Unlike
144 // other locks, this one can generally be held long-term because we never expect any of these
145 // static data structures to be referenced outside of the worker thread except on the first
146 // load after configuration change.
147 static final Object sBgLock = new Object();
148
149 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
150 // LauncherModel to their ids
151 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
152
153 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
154 // created by LauncherModel that are directly on the home screen (however, no widgets or
155 // shortcuts within folders).
156 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
157
158 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
159 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
160 new ArrayList<LauncherAppWidgetInfo>();
161
162 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
163 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
164
165 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
166 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
167
168 // sBgWorkspaceScreens is the ordered set of workspace screens.
169 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
170
171 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
172 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
173 new HashMap<UserHandleCompat, HashSet<String>>();
174
175 // </ only access in worker thread >
176
177 private IconCache mIconCache;
178
179 protected int mPreviousConfigMcc;
180
181 private final LauncherAppsCompat mLauncherApps;
182 private final UserManagerCompat mUserManager;
183
184 public interface Callbacks {
185 public boolean setLoadOnResume();
186 public int getCurrentWorkspaceScreen();
187 public void startBinding();
188 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
189 boolean forceAnimateIcons);
190 public void bindScreens(ArrayList<Long> orderedScreenIds);
191 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
192 public void bindFolders(HashMap<Long,FolderInfo> folders);
193 public void finishBindingItems(boolean upgradePath);
194 public void bindAppWidget(LauncherAppWidgetInfo info);
195 public void bindAllApplications(ArrayList<AppInfo> apps);
196 public void bindAppsAdded(ArrayList<Long> newScreens,
197 ArrayList<ItemInfo> addNotAnimated,
198 ArrayList<ItemInfo> addAnimated,
199 ArrayList<AppInfo> addedApps);
200 public void bindAppsUpdated(ArrayList<AppInfo> apps);
201 public void bindAppsRestored(ArrayList<AppInfo> apps);
202 public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
203 public void updatePackageBadge(String packageName);
204 public void bindComponentsRemoved(ArrayList<String> packageNames,
205 ArrayList<AppInfo> appInfos, UserHandleCompat user);
206 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
207 public void bindSearchablesChanged();
208 public boolean isAllAppsButtonRank(int rank);
209 public void onPageBoundSynchronously(int page);
210 public void dumpLogsToLocalData();
211 }
212
213 public interface ItemInfoFilter {
214 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
215 }
216
217 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
218 Context context = app.getContext();
219
220 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
221 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
222 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
223 // resource string.
224 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
225 ProviderInfo providerInfo =
226 context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
227 ProviderInfo redirectProvider =
228 context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
229
230 Log.d(TAG, "Old launcher provider: " + oldProvider);
231 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
232
233 if (mOldContentProviderExists) {
234 Log.d(TAG, "Old launcher provider exists.");
235 } else {
236 Log.d(TAG, "Old launcher provider does not exist.");
237 }
238
239 mApp = app;
240 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
241 mIconCache = iconCache;
242
243 final Resources res = context.getResources();
244 Configuration config = res.getConfiguration();
245 mPreviousConfigMcc = config.mcc;
246 mLauncherApps = LauncherAppsCompat.getInstance(context);
247 mUserManager = UserManagerCompat.getInstance(context);
248 }
249
250 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
251 * posted on the main thread handler. */
252 private void runOnMainThread(Runnable r) {
253 runOnMainThread(r, 0);
254 }
255 private void runOnMainThread(Runnable r, int type) {
256 if (sWorkerThread.getThreadId() == Process.myTid()) {
257 // If we are on the worker thread, post onto the main handler
258 mHandler.post(r);
259 } else {
260 r.run();
261 }
262 }
263
264 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
265 * posted on the worker thread handler. */
266 private static void runOnWorkerThread(Runnable r) {
267 if (sWorkerThread.getThreadId() == Process.myTid()) {
268 r.run();
269 } else {
270 // If we are not on the worker thread, then post to the worker handler
271 sWorker.post(r);
272 }
273 }
274
275 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
276 return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
277 }
278
279 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
280 long screen) {
281 LauncherAppState app = LauncherAppState.getInstance();
282 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
283 final int xCount = (int) grid.numColumns;
284 final int yCount = (int) grid.numRows;
285 boolean[][] occupied = new boolean[xCount][yCount];
286
287 int cellX, cellY, spanX, spanY;
288 for (int i = 0; i < items.size(); ++i) {
289 final ItemInfo item = items.get(i);
290 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
291 if (item.screenId == screen) {
292 cellX = item.cellX;
293 cellY = item.cellY;
294 spanX = item.spanX;
295 spanY = item.spanY;
296 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
297 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
298 occupied[x][y] = true;
299 }
300 }
301 }
302 }
303 }
304
305 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
306 }
307 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
308 Intent launchIntent,
309 int firstScreenIndex,
310 ArrayList<Long> workspaceScreens) {
311 // Lock on the app so that we don't try and get the items while apps are being added
312 LauncherAppState app = LauncherAppState.getInstance();
313 LauncherModel model = app.getModel();
314 boolean found = false;
315 synchronized (app) {
316 if (sWorkerThread.getThreadId() != Process.myTid()) {
317 // Flush the LauncherModel worker thread, so that if we just did another
318 // processInstallShortcut, we give it time for its shortcut to get added to the
319 // database (getItemsInLocalCoordinates reads the database)
320 model.flushWorkerThread();
321 }
322 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
323
324 // Try adding to the workspace screens incrementally, starting at the default or center
325 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
326 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
327 int count = workspaceScreens.size();
328 for (int screen = firstScreenIndex; screen < count && !found; screen++) {
329 int[] tmpCoordinates = new int[2];
330 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
331 workspaceScreens.get(screen))) {
332 // Update the Launcher db
333 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
334 }
335 }
336 }
337 return null;
338 }
339
340 <<<<<<< GitAnalyzerPlus_ours
341 public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
342 // Process the updated package state
343 Runnable r = new Runnable() {
344 public void run() {
345 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
346 if (callbacks != null) {
347 callbacks.updatePackageState(installInfo);
348 }
349 }
350 };
351 mHandler.post(r);
352 }
353
354 public void updatePackageBadge(final String packageName) {
355 // Process the updated package badge
356 Runnable r = new Runnable() {
357 public void run() {
358 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
359 if (callbacks != null) {
360 callbacks.updatePackageBadge(packageName);
361 }
362 }
363 };
364 mHandler.post(r);
365 }
366
367 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
368 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
369
370 if (allAppsApps == null) {
371 throw new RuntimeException("allAppsApps must not be null");
372 }
373 if (allAppsApps.isEmpty()) {
374 return;
375 }
376
377 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
378 Iterator<AppInfo> iter = allAppsApps.iterator();
379 while (iter.hasNext()) {
380 ItemInfo a = iter.next();
381 if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
382 restoredAppsFinal.add((AppInfo) a);
383 }
384 }
385
386 // Process the newly added applications and add them to the database first
387 Runnable r = new Runnable() {
388 public void run() {
389 runOnMainThread(new Runnable() {
390 public void run() {
391 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
392 if (callbacks == cb && cb != null) {
393 if (!restoredAppsFinal.isEmpty()) {
394 for (AppInfo info : restoredAppsFinal) {
395 final Intent intent = info.getIntent();
396 if (intent != null) {
397 mIconCache.deletePreloadedIcon(intent.getComponent(),
398 info.user);
399 }
400 }
401 callbacks.bindAppsUpdated(restoredAppsFinal);
402 }
403 callbacks.bindAppsAdded(null, null, null, allAppsApps);
404 }
405 }
406 });
407 }
408 };
409 runOnWorkerThread(r);
410 ||||||| GitAnalyzerPlus_base
411 <<<<<<< OURS
412 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
413 final ArrayList<AppInfo> allAppsApps) {
414 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
415 addAndBindAddedApps(context, workspaceApps, cb, allAppsApps);
416 =======
417 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
418 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
419
420 if (allAppsApps == null) {
421 throw new RuntimeException("allAppsApps must not be null");
422 }
423 if (allAppsApps.isEmpty()) {
424 return;
425 }
426
427 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
428 Iterator<AppInfo> iter = allAppsApps.iterator();
429 while (iter.hasNext()) {
430 ItemInfo a = iter.next();
431 if (LauncherModel.appWasRestored(ctx, a.getIntent())) {
432 restoredAppsFinal.add((AppInfo) a);
433 }
434 }
435
436 // Process the newly added applications and add them to the database first
437 Runnable r = new Runnable() {
438 public void run() {
439 runOnMainThread(new Runnable() {
440 public void run() {
441 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
442 if (callbacks == cb && cb != null) {
443 callbacks.bindAppsAdded(null, null, null, allAppsApps);
444 if (!restoredAppsFinal.isEmpty()) {
445 callbacks.bindAppsUpdated(restoredAppsFinal);
446 }
447 }
448 }
449 });
450 }
451 };
452 runOnWorkerThread(r);
453 >>>>>>> THEIRS
454 }
455 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
456 final Callbacks callbacks, final ArrayList<AppInfo> allAppsApps) {
457 if (workspaceApps == null || allAppsApps == null) {
458 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
459 }
460 if (workspaceApps.isEmpty() && allAppsApps.isEmpty()) {
461 return;
462 }
463 // Process the newly added applications and add them to the database first
464 Runnable r = new Runnable() {
465 public void run() {
466 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
467 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
468 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
469
470 // Get the list of workspace screens. We need to append to this list and
471 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
472 // called.
473 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
474 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
475 for (Integer i : orderedScreens.keySet()) {
476 long screenId = orderedScreens.get(i);
477 workspaceScreens.add(screenId);
478 }
479
480 synchronized(sBgLock) {
481 Iterator<ItemInfo> iter = workspaceApps.iterator();
482 while (iter.hasNext()) {
483 ItemInfo a = iter.next();
484 final String name = a.title.toString();
485 final Intent launchIntent = a.getIntent();
486
487 // Short-circuit this logic if the icon exists somewhere on the workspace
488 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
489 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
490 if (a instanceof AppInfo &&
491 LauncherModel.appWasRestored(context, launchIntent)) {
492 restoredAppsFinal.add((AppInfo) a);
493 }
494 continue;
495 }
496
497 // Add this icon to the db, creating a new page if necessary. If there
498 // is only the empty page then we just add items to the first page.
499 // Otherwise, we add them to the next pages.
500 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
501 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
502 name, launchIntent, startSearchPageIndex, workspaceScreens);
503 if (coords == null) {
504 LauncherProvider lp = LauncherAppState.getLauncherProvider();
505
506 // If we can't find a valid position, then just add a new screen.
507 // This takes time so we need to re-queue the add until the new
508 // page is added. Create as many screens as necessary to satisfy
509 // the startSearchPageIndex.
510 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
511 workspaceScreens.size());
512 while (numPagesToAdd > 0) {
513 long screenId = lp.generateNewScreenId();
514 // Save the screen id for binding in the workspace
515 workspaceScreens.add(screenId);
516 addedWorkspaceScreensFinal.add(screenId);
517 numPagesToAdd--;
518 }
519
520 // Find the coordinate again
521 coords = LauncherModel.findNextAvailableIconSpace(context,
522 name, launchIntent, startSearchPageIndex, workspaceScreens);
523 }
524 if (coords == null) {
525 =======
526 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
527 final ArrayList<AppInfo> allAppsApps) {
528 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
529 addAndBindAddedApps(context, workspaceApps, cb, allAppsApps);
530 >>>>>>> GitAnalyzerPlus_theirs
531 }
532
533 public void addAndBindAddedWorkspaceApps(final Context context,
534 final ArrayList<ItemInfo> workspaceApps) {
535 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
536
537 if (workspaceApps == null) {
538 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
539 }
540 if (workspaceApps.isEmpty()) {
541 return;
542 }
543 // Process the newly added applications and add them to the database first
544 Runnable r = new Runnable() {
545 public void run() {
546 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
547 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
548
549 // Get the list of workspace screens. We need to append to this list and
550 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
551 // called.
552 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
553 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
554 for (Integer i : orderedScreens.keySet()) {
555 long screenId = orderedScreens.get(i);
556 workspaceScreens.add(screenId);
557 }
558
559 synchronized(sBgLock) {
560 Iterator<ItemInfo> iter = workspaceApps.iterator();
561 while (iter.hasNext()) {
562 ItemInfo a = iter.next();
563 final String name = a.title.toString();
564 final Intent launchIntent = a.getIntent();
565
566 // Short-circuit this logic if the icon exists somewhere on the workspace
567 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
568 <<<<<<< GitAnalyzerPlus_ours
569 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
570 if (a instanceof AppInfo &&
571 LauncherModel.appWasPromise(context, launchIntent, a.user)) {
572 restoredAppsFinal.add((AppInfo) a);
573 }
574 ||||||| GitAnalyzerPlus_base
575 });
576 }
577 };
578 runOnWorkerThread(r);
579 >>>>>>> THEIRS
580 }
581 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
582 final Callbacks callbacks, final ArrayList<AppInfo> allAppsApps) {
583 if (workspaceApps == null || allAppsApps == null) {
584 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
585 }
586 if (workspaceApps.isEmpty() && allAppsApps.isEmpty()) {
587 return;
588 }
589 // Process the newly added applications and add them to the database first
590 Runnable r = new Runnable() {
591 public void run() {
592 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
593 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
594 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
595
596 // Get the list of workspace screens. We need to append to this list and
597 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
598 // called.
599 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
600 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
601 for (Integer i : orderedScreens.keySet()) {
602 long screenId = orderedScreens.get(i);
603 workspaceScreens.add(screenId);
604 }
605
606 synchronized(sBgLock) {
607 Iterator<ItemInfo> iter = workspaceApps.iterator();
608 while (iter.hasNext()) {
609 ItemInfo a = iter.next();
610 final String name = a.title.toString();
611 final Intent launchIntent = a.getIntent();
612
613 // Short-circuit this logic if the icon exists somewhere on the workspace
614 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
615 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
616 if (a instanceof AppInfo &&
617 LauncherModel.appWasRestored(context, launchIntent)) {
618 restoredAppsFinal.add((AppInfo) a);
619 }
620 continue;
621 }
622
623 // Add this icon to the db, creating a new page if necessary. If there
624 // is only the empty page then we just add items to the first page.
625 // Otherwise, we add them to the next pages.
626 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
627 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
628 name, launchIntent, startSearchPageIndex, workspaceScreens);
629 if (coords == null) {
630 LauncherProvider lp = LauncherAppState.getLauncherProvider();
631
632 // If we can't find a valid position, then just add a new screen.
633 // This takes time so we need to re-queue the add until the new
634 // page is added. Create as many screens as necessary to satisfy
635 // the startSearchPageIndex.
636 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
637 workspaceScreens.size());
638 while (numPagesToAdd > 0) {
639 long screenId = lp.generateNewScreenId();
640 // Save the screen id for binding in the workspace
641 workspaceScreens.add(screenId);
642 addedWorkspaceScreensFinal.add(screenId);
643 numPagesToAdd--;
644 }
645
646 // Find the coordinate again
647 coords = LauncherModel.findNextAvailableIconSpace(context,
648 name, launchIntent, startSearchPageIndex, workspaceScreens);
649 }
650 if (coords == null) {
651 throw new RuntimeException("Coordinates should not be null");
652 }
653
654 ShortcutInfo shortcutInfo;
655 if (a instanceof ShortcutInfo) {
656 shortcutInfo = (ShortcutInfo) a;
657 } else if (a instanceof AppInfo) {
658 shortcutInfo = ((AppInfo) a).makeShortcut();
659 } else {
660 throw new RuntimeException("Unexpected info type");
661 }
662
663 // Add the shortcut to the db
664 addItemToDatabase(context, shortcutInfo,
665 LauncherSettings.Favorites.CONTAINER_DESKTOP,
666 coords.first, coords.second[0], coords.second[1], false);
667 // Save the ShortcutInfo for binding in the workspace
668 addedShortcutsFinal.add(shortcutInfo);
669 }
670 }
671
672 // Update the workspace screens
673 updateWorkspaceScreenOrder(context, workspaceScreens);
674
675 if (!addedShortcutsFinal.isEmpty() || !allAppsApps.isEmpty()) {
676 runOnMainThread(new Runnable() {
677 public void run() {
678 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
679 if (callbacks == cb && cb != null) {
680 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
681 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
682 if (!addedShortcutsFinal.isEmpty()) {
683 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 🔵
684 long lastScreenId = info.screenId;
685 for (ItemInfo i : addedShortcutsFinal) {
686 if (i.screenId == lastScreenId) {
687 addAnimated.add(i);
688 } else {
689 addNotAnimated.add(i);
690 }
691 }
692 }
693 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
694 =======
695 >>>>>>> GitAnalyzerPlus_theirs
696 continue;
697 }
698
699 // Add this icon to the db, creating a new page if necessary. If there
700 // is only the empty page then we just add items to the first page.
701 // Otherwise, we add them to the next pages.
702 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
703 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
704 name, launchIntent, startSearchPageIndex, workspaceScreens);
705 if (coords == null) {
706 LauncherProvider lp = LauncherAppState.getLauncherProvider();
707
708 // If we can't find a valid position, then just add a new screen.
709 // This takes time so we need to re-queue the add until the new
710 // page is added. Create as many screens as necessary to satisfy
711 // the startSearchPageIndex.
712 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
713 workspaceScreens.size());
714 while (numPagesToAdd > 0) {
715 long screenId = lp.generateNewScreenId();
716 // Save the screen id for binding in the workspace
717 workspaceScreens.add(screenId);
718 addedWorkspaceScreensFinal.add(screenId);
719 numPagesToAdd--;
720 }
721
722 // Find the coordinate again
723 coords = LauncherModel.findNextAvailableIconSpace(context,
724 name, launchIntent, startSearchPageIndex, workspaceScreens);
725 }
726 if (coords == null) {
727 throw new RuntimeException("Coordinates should not be null");
728 }
729
730 ShortcutInfo shortcutInfo;
731 if (a instanceof ShortcutInfo) {
732 shortcutInfo = (ShortcutInfo) a;
733 } else if (a instanceof AppInfo) {
734 shortcutInfo = ((AppInfo) a).makeShortcut();
735 } else {
736 throw new RuntimeException("Unexpected info type");
737 }
738
739 // Add the shortcut to the db
740 addItemToDatabase(context, shortcutInfo,
741 LauncherSettings.Favorites.CONTAINER_DESKTOP,
742 coords.first, coords.second[0], coords.second[1], false);
743 // Save the ShortcutInfo for binding in the workspace
744 addedShortcutsFinal.add(shortcutInfo);
745 }
746 }
747
748 // Update the workspace screens
749 updateWorkspaceScreenOrder(context, workspaceScreens);
750
751 if (!addedShortcutsFinal.isEmpty()) {
752 runOnMainThread(new Runnable() {
753 public void run() {
754 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
755 if (callbacks == cb && cb != null) {
756 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
757 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
758 if (!addedShortcutsFinal.isEmpty()) {
759 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 🔵
760 long lastScreenId = info.screenId;
761 for (ItemInfo i : addedShortcutsFinal) {
762 if (i.screenId == lastScreenId) {
763 addAnimated.add(i);
764 } else {
765 addNotAnimated.add(i);
766 }
767 }
768 }
769 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
770 <<<<<<< GitAnalyzerPlus_ours
771 addNotAnimated, addAnimated, null);
772 if (!restoredAppsFinal.isEmpty()) {
773 callbacks.bindAppsUpdated(restoredAppsFinal);
774 }
775 ||||||| GitAnalyzerPlus_base
776 }
777 if (coords == null) {
778 throw new RuntimeException("Coordinates should not be null");
779 }
780
781 ShortcutInfo shortcutInfo;
782 if (a instanceof ShortcutInfo) {
783 shortcutInfo = (ShortcutInfo) a;
784 } else if (a instanceof AppInfo) {
785 shortcutInfo = ((AppInfo) a).makeShortcut();
786 } else {
787 throw new RuntimeException("Unexpected info type");
788 }
789
790 // Add the shortcut to the db
791 addItemToDatabase(context, shortcutInfo,
792 LauncherSettings.Favorites.CONTAINER_DESKTOP,
793 coords.first, coords.second[0], coords.second[1], false);
794 // Save the ShortcutInfo for binding in the workspace
795 addedShortcutsFinal.add(shortcutInfo);
796 }
797 }
798
799 // Update the workspace screens
800 updateWorkspaceScreenOrder(context, workspaceScreens);
801
802 if (!addedShortcutsFinal.isEmpty() || !allAppsApps.isEmpty()) {
803 runOnMainThread(new Runnable() {
804 public void run() {
805 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
806 if (callbacks == cb && cb != null) {
807 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
808 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
809 if (!addedShortcutsFinal.isEmpty()) {
810 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 🔵
811 long lastScreenId = info.screenId;
812 for (ItemInfo i : addedShortcutsFinal) {
813 if (i.screenId == lastScreenId) {
814 addAnimated.add(i);
815 } else {
816 addNotAnimated.add(i);
817 }
818 }
819 }
820 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
821 <<<<<<< OURS
822 addNotAnimated, addAnimated, allAppsApps);
823 =======
824 addNotAnimated, addAnimated, null);
825 if (!restoredAppsFinal.isEmpty()) {
826 callbacks.bindAppsUpdated(restoredAppsFinal);
827 }
828 >>>>>>> THEIRS
829 }
830 }
831 });
832 }
833 }
834 };
835 runOnWorkerThread(r);
836 }
837
838 public Bitmap getFallbackIcon() {
839 if (mDefaultIcon == null) {
840 final Context context = LauncherAppState.getInstance().getContext();
841 mDefaultIcon = Utilities.createIconBitmap(
842 mIconCache.getFullResDefaultActivityIcon(), context);
843 }
844 return Bitmap.createBitmap(mDefaultIcon);
845 }
846
847 public void unbindItemInfosAndClearQueuedBindRunnables() {
848 if (sWorkerThread.getThreadId() == Process.myTid()) {
849 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
850 "main thread");
851 }
852
853 // Clear any deferred bind runnables
854 mDeferredBindRunnables.clear();
855 // Remove any queued bind runnables
856 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
857 // Unbind all the workspace items
858 unbindWorkspaceItemsOnMainThread();
859 }
860
861 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
862 void unbindWorkspaceItemsOnMainThread() {
863 // Ensure that we don't use the same workspace items data structure on the main thread
864 // by making a copy of workspace items first.
865 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
866 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
867 synchronized (sBgLock) {
868 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
869 tmpAppWidgets.addAll(sBgAppWidgets);
870 }
871 Runnable r = new Runnable() {
872 @Override
873 public void run() {
874 for (ItemInfo item : tmpWorkspaceItems) {
875 item.unbind();
876 }
877 for (ItemInfo item : tmpAppWidgets) {
878 item.unbind();
879 }
880 }
881 };
882 runOnMainThread(r);
883 }
884
885 /**
886 * Adds an item to the DB if it was not created previously, or move it to a new
887 * <container, screen, cellX, cellY>
888 */
889 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
890 long screenId, int cellX, int cellY) {
891 if (item.container == ItemInfo.NO_ID) {
892 // From all apps
893 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
894 } else {
895 // From somewhere else
896 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
897 }
898 }
899 =======
900 addNotAnimated, addAnimated, allAppsApps);
901 >>>>>>> GitAnalyzerPlus_theirs
902 }
903 }
904 });
905 }
906 }
907 };
908 runOnWorkerThread(r);
909 }
910
911 public void unbindItemInfosAndClearQueuedBindRunnables() {
912 if (sWorkerThread.getThreadId() == Process.myTid()) {
913 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
914 "main thread");
915 }
916
917 // Clear any deferred bind runnables
918 synchronized (mDeferredBindRunnables) {
919 mDeferredBindRunnables.clear();
920 }
921 // Remove any queued bind runnables
922 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
923 // Unbind all the workspace items
924 unbindWorkspaceItemsOnMainThread();
925 }
926
927 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
928 void unbindWorkspaceItemsOnMainThread() {
929 // Ensure that we don't use the same workspace items data structure on the main thread
930 // by making a copy of workspace items first.
931 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
932 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
933 synchronized (sBgLock) {
934 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
935 tmpAppWidgets.addAll(sBgAppWidgets);
936 }
937 Runnable r = new Runnable() {
938 @Override
939 public void run() {
940 for (ItemInfo item : tmpWorkspaceItems) {
941 item.unbind();
942 }
943 for (ItemInfo item : tmpAppWidgets) {
944 item.unbind();
945 }
946 }
947 };
948 runOnMainThread(r);
949 }
950
951 /**
952 * Adds an item to the DB if it was not created previously, or move it to a new
953 * <container, screen, cellX, cellY>
954 */
955 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
956 long screenId, int cellX, int cellY) {
957 if (item.container == ItemInfo.NO_ID) {
958 // From all apps
959 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
960 } else {
961 // From somewhere else
962 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
963 }
964 }
965
966 static void checkItemInfoLocked(
967 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
968 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
969 if (modelItem != null && item != modelItem) {
970 // check all the data is consistent
971 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
972 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
973 ShortcutInfo shortcut = (ShortcutInfo) item;
974 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
975 modelShortcut.intent.filterEquals(shortcut.intent) &&
976 modelShortcut.id == shortcut.id &&
977 modelShortcut.itemType == shortcut.itemType &&
978 modelShortcut.container == shortcut.container &&
979 modelShortcut.screenId == shortcut.screenId &&
980 modelShortcut.cellX == shortcut.cellX &&
981 modelShortcut.cellY == shortcut.cellY &&
982 modelShortcut.spanX == shortcut.spanX &&
983 modelShortcut.spanY == shortcut.spanY &&
984 ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
985 (modelShortcut.dropPos != null &&
986 shortcut.dropPos != null &&
987 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
988 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
989 // For all intents and purposes, this is the same object
990 return;
991 }
992 }
993
994 // the modelItem needs to match up perfectly with item if our model is
995 // to be consistent with the database-- for now, just require
996 // modelItem == item or the equality check above
997 String msg = "item: " + ((item != null) ? item.toString() : "null") +
998 "modelItem: " +
999 ((modelItem != null) ? modelItem.toString() : "null") +
1000 "Error: ItemInfo passed to checkItemInfo doesn't match original";
1001 RuntimeException e = new RuntimeException(msg);
1002 if (stackTrace != null) {
1003 e.setStackTrace(stackTrace);
1004 }
1005 throw e;
1006 }
1007 }
1008
1009 static void checkItemInfo(final ItemInfo item) {
1010 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1011 final long itemId = item.id;
1012 Runnable r = new Runnable() {
1013 public void run() {
1014 synchronized (sBgLock) {
1015 checkItemInfoLocked(itemId, item, stackTrace);
1016 }
1017 }
1018 };
1019 runOnWorkerThread(r);
1020 }
1021
1022 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
1023 final ItemInfo item, final String callingFunction) {
1024 final long itemId = item.id;
1025 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
1026 final ContentResolver cr = context.getContentResolver();
1027
1028 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1029 Runnable r = new Runnable() {
1030 public void run() {
1031 cr.update(uri, values, null, null);
1032 updateItemArrays(item, itemId, stackTrace);
1033 }
1034 };
1035 runOnWorkerThread(r);
1036 }
1037
1038 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
1039 final ArrayList<ItemInfo> items, final String callingFunction) {
1040 final ContentResolver cr = context.getContentResolver();
1041
1042 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1043 Runnable r = new Runnable() {
1044 public void run() {
1045 ArrayList<ContentProviderOperation> ops =
1046 new ArrayList<ContentProviderOperation>();
1047 int count = items.size();
1048 for (int i = 0; i < count; i++) {
1049 ItemInfo item = items.get(i);
1050 final long itemId = item.id;
1051 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
1052 ContentValues values = valuesList.get(i);
1053
1054 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
1055 updateItemArrays(item, itemId, stackTrace);
1056
1057 }
1058 try {
1059 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1060 } catch (Exception e) {
1061 e.printStackTrace();
1062 }
1063 }
1064 };
1065 runOnWorkerThread(r);
1066 }
1067
1068 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
1069 // Lock on mBgLock *after* the db operation
1070 synchronized (sBgLock) {
1071 checkItemInfoLocked(itemId, item, stackTrace);
1072
1073 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1074 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1075 // Item is in a folder, make sure this folder exists
1076 if (!sBgFolders.containsKey(item.container)) {
1077 // An items container is being set to a that of an item which is not in
1078 // the list of Folders.
1079 String msg = "item: " + item + " container being set to: " +
1080 item.container + ", not in the list of folders";
1081 Log.e(TAG, msg);
1082 }
1083 }
1084
1085 // Items are added/removed from the corresponding FolderInfo elsewhere, such
1086 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
1087 // that are on the desktop, as appropriate
1088 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
1089 if (modelItem != null &&
1090 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
1091 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
1092 switch (modelItem.itemType) {
1093 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1094 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1095 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1096 if (!sBgWorkspaceItems.contains(modelItem)) {
1097 sBgWorkspaceItems.add(modelItem);
1098 }
1099 break;
1100 default:
1101 break;
1102 }
1103 } else {
1104 sBgWorkspaceItems.remove(modelItem);
1105 }
1106 }
1107 }
1108
1109 public void flushWorkerThread() {
1110 mFlushingWorkerThread = true;
1111 Runnable waiter = new Runnable() {
1112 public void run() {
1113 synchronized (this) {
1114 notifyAll();
1115 mFlushingWorkerThread = false;
1116 }
1117 }
1118 };
1119
1120 synchronized(waiter) {
1121 runOnWorkerThread(waiter);
1122 if (mLoaderTask != null) {
1123 synchronized(mLoaderTask) {
1124 mLoaderTask.notify();
1125 }
1126 }
1127 boolean success = false;
1128 while (!success) {
1129 try {
1130 waiter.wait();
1131 success = true;
1132 } catch (InterruptedException e) {
1133 }
1134 }
1135 }
1136 }
1137
1138 /**
1139 * Move an item in the DB to a new <container, screen, cellX, cellY>
1140 */
1141 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
1142 final long screenId, final int cellX, final int cellY) {
1143 item.container = container;
1144 item.cellX = cellX;
1145 item.cellY = cellY;
1146
1147 // We store hotseat items in canonical form which is this orientation invariant position
1148 // in the hotseat
1149 if (context instanceof Launcher && screenId < 0 &&
1150 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1151 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
1152 } else {
1153 item.screenId = screenId;
1154 }
1155
1156 final ContentValues values = new ContentValues();
1157 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1158 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1159 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1160 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
1161
1162 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
1163 }
1164
1165 /**
1166 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
1167 * cellX, cellY have already been updated on the ItemInfos.
1168 */
1169 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
1170 final long container, final int screen) {
1171
1172 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
1173 int count = items.size();
1174
1175 for (int i = 0; i < count; i++) {
1176 ItemInfo item = items.get(i);
1177 item.container = container;
1178
1179 // We store hotseat items in canonical form which is this orientation invariant position
1180 // in the hotseat
1181 if (context instanceof Launcher && screen < 0 &&
1182 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1183 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
1184 item.cellY);
1185 } else {
1186 item.screenId = screen;
1187 }
1188
1189 final ContentValues values = new ContentValues();
1190 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1191 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1192 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1193 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
1194
1195 contentValues.add(values);
1196 }
1197 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
1198 }
1199
1200 /**
1201 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
1202 */
1203 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
1204 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
1205 item.container = container;
1206 item.cellX = cellX;
1207 item.cellY = cellY;
1208 item.spanX = spanX;
1209 item.spanY = spanY;
1210
1211 // We store hotseat items in canonical form which is this orientation invariant position
1212 // in the hotseat
1213 if (context instanceof Launcher && screenId < 0 &&
1214 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1215 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
1216 } else {
1217 item.screenId = screenId;
1218 }
1219
1220 final ContentValues values = new ContentValues();
1221 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1222 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1223 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1224 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
1225 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
1226 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
1227
1228 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
1229 }
1230
1231 /**
1232 * Update an item to the database in a specified container.
1233 */
1234 static void updateItemInDatabase(Context context, final ItemInfo item) {
1235 final ContentValues values = new ContentValues();
1236 item.onAddToDatabase(context, values);
1237 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
1238 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
1239 }
1240
1241 /**
1242 * Returns true if the shortcuts already exists in the database.
1243 * we identify a shortcut by its title and intent.
1244 */
1245 static boolean shortcutExists(Context context, String title, Intent intent) {
1246 final ContentResolver cr = context.getContentResolver();
1247 final Intent intentWithPkg, intentWithoutPkg;
1248
1249 if (intent.getComponent() != null) {
1250 // If component is not null, an intent with null package will produce
1251 // the same result and should also be a match.
1252 if (intent.getPackage() != null) {
1253 intentWithPkg = intent;
1254 intentWithoutPkg = new Intent(intent).setPackage(null);
1255 } else {
1256 intentWithPkg = new Intent(intent).setPackage(
1257 intent.getComponent().getPackageName());
1258 intentWithoutPkg = intent;
1259 }
1260 } else {
1261 intentWithPkg = intent;
1262 intentWithoutPkg = intent;
1263 }
1264 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
1265 new String[] { "title", "intent" }, "title=? and (intent=? or intent=?)",
1266 new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null);
1267 boolean result = false;
1268 try {
1269 result = c.moveToFirst();
1270 } finally {
1271 c.close();
1272 }
1273 return result;
1274 }
1275
1276 /**
1277 <<<<<<< GitAnalyzerPlus_ours
1278 * Returns true if the promise shortcuts with the same package name exists on the workspace.
1279 */
1280 static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
1281 final ComponentName component = intent.getComponent();
1282 if (component == null) {
1283 return false;
1284 }
1285 return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
1286 }
1287
1288 /**
1289 ||||||| GitAnalyzerPlus_base
1290 item.spanX = spanX;
1291 item.spanY = spanY;
1292
1293 // We store hotseat items in canonical form which is this orientation invariant position
1294 // in the hotseat
1295 if (context instanceof Launcher && screenId < 0 &&
1296 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1297 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
1298 } else {
1299 item.screenId = screenId;
1300 }
1301
1302 final ContentValues values = new ContentValues();
1303 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
1304 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
1305 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
1306 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
1307 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
1308 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
1309
1310 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
1311 }
1312
1313 /**
1314 * Update an item to the database in a specified container.
1315 */
1316 static void updateItemInDatabase(Context context, final ItemInfo item) {
1317 final ContentValues values = new ContentValues();
1318 item.onAddToDatabase(values);
1319 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
1320 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
1321 }
1322
1323 /**
1324 * Returns true if the shortcuts already exists in the database.
1325 * we identify a shortcut by its title and intent.
1326 */
1327 static boolean shortcutExists(Context context, String title, Intent intent) {
1328 final ContentResolver cr = context.getContentResolver();
1329 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
1330 new String[] { "title", "intent" }, "title=? and intent=?",
1331 new String[] { title, intent.toUri(0) }, null);
1332 boolean result = false;
1333 try {
1334 result = c.moveToFirst();
1335 } finally {
1336 c.close();
1337 }
1338 return result;
1339 }
1340
1341 /**
1342 * Returns true if the shortcuts already exists in the database.
1343 * we identify a shortcut by the component name of the intent.
1344 */
1345 static boolean appWasRestored(Context context, Intent intent) {
1346 final ContentResolver cr = context.getContentResolver();
1347 final ComponentName component = intent.getComponent();
1348 if (component == null) {
1349 return false;
1350 }
1351 String componentName = component.flattenToString();
1352 final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1";
1353 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
1354 new String[]{"intent", "restored"}, where, null, null);
1355 boolean result = false;
1356 try {
1357 result = c.moveToFirst();
1358 } finally {
1359 c.close();
1360 }
1361 Log.d(TAG, "shortcutWasRestored is " + result + " for " + componentName);
1362 return result;
1363 }
1364
1365 /**
1366 * Returns an ItemInfo array containing all the items in the LauncherModel.
1367 * The ItemInfo.id is not set through this function.
1368 */
1369 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
1370 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1371 final ContentResolver cr = context.getContentResolver();
1372 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
1373 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
1374 LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Fav🔵
1375 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
1376
1377 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1378 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1379 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1380 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1381 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1382 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
1383 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
1384
1385 try {
1386 while (c.moveToNext()) {
1387 ItemInfo item = new ItemInfo();
1388 item.cellX = c.getInt(cellXIndex);
1389 item.cellY = c.getInt(cellYIndex);
1390 item.spanX = Math.max(1, c.getInt(spanXIndex));
1391 item.spanY = Math.max(1, c.getInt(spanYIndex));
1392 item.container = c.getInt(containerIndex);
1393 item.itemType = c.getInt(itemTypeIndex);
1394 item.screenId = c.getInt(screenIndex);
1395
1396 items.add(item);
1397 }
1398 } catch (Exception e) {
1399 items.clear();
1400 } finally {
1401 c.close();
1402 }
1403
1404 return items;
1405 }
1406
1407 /**
1408 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
1409 */
1410 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
1411 final ContentResolver cr = context.getContentResolver();
1412 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
1413 "_id=? and (itemType=? or itemType=?)",
1414 new String[] { String.valueOf(id),
1415 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
1416
1417 try {
1418 if (c.moveToFirst()) {
1419 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1420 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1421 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1422 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1423 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1424 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1425
1426 FolderInfo folderInfo = null;
1427 switch (c.getInt(itemTypeIndex)) {
1428 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1429 folderInfo = findOrMakeFolder(folderList, id);
1430 break;
1431 }
1432
1433 =======
1434 >>>>>>> GitAnalyzerPlus_theirs
1435 * Returns an ItemInfo array containing all the items in the LauncherModel.
1436 * The ItemInfo.id is not set through this function.
1437 */
1438 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
1439 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1440 final ContentResolver cr = context.getContentResolver();
1441 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
1442 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
1443 LauncherSettings.Favorites.SCREEN,
1444 LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
1445 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
1446 LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
1447
1448 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1449 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1450 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1451 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1452 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1453 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
1454 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
1455 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
1456 UserManagerCompat userManager = UserManagerCompat.getInstance(context);
1457 try {
1458 while (c.moveToNext()) {
1459 ItemInfo item = new ItemInfo();
1460 item.cellX = c.getInt(cellXIndex);
1461 item.cellY = c.getInt(cellYIndex);
1462 item.spanX = Math.max(1, c.getInt(spanXIndex));
1463 item.spanY = Math.max(1, c.getInt(spanYIndex));
1464 item.container = c.getInt(containerIndex);
1465 item.itemType = c.getInt(itemTypeIndex);
1466 item.screenId = c.getInt(screenIndex);
1467 long serialNumber = c.getInt(profileIdIndex);
1468 item.user = userManager.getUserForSerialNumber(serialNumber);
1469 // Skip if user has been deleted.
1470 if (item.user != null) {
1471 items.add(item);
1472 }
1473 }
1474 } catch (Exception e) {
1475 items.clear();
1476 } finally {
1477 c.close();
1478 }
1479
1480 return items;
1481 }
1482
1483 /**
1484 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
1485 */
1486 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
1487 final ContentResolver cr = context.getContentResolver();
1488 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
1489 "_id=? and (itemType=? or itemType=?)",
1490 new String[] { String.valueOf(id),
1491 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
1492
1493 try {
1494 if (c.moveToFirst()) {
1495 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1496 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1497 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1498 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1499 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1500 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1501
1502 FolderInfo folderInfo = null;
1503 switch (c.getInt(itemTypeIndex)) {
1504 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1505 folderInfo = findOrMakeFolder(folderList, id);
1506 break;
1507 }
1508
1509 folderInfo.title = c.getString(titleIndex);
1510 folderInfo.id = id;
1511 folderInfo.container = c.getInt(containerIndex);
1512 folderInfo.screenId = c.getInt(screenIndex);
1513 folderInfo.cellX = c.getInt(cellXIndex);
1514 folderInfo.cellY = c.getInt(cellYIndex);
1515
1516 return folderInfo;
1517 }
1518 } finally {
1519 c.close();
1520 }
1521
1522 return null;
1523 }
1524
1525 /**
1526 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1527 * cellY fields of the item. Also assigns an ID to the item.
1528 */
1529 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
1530 final long screenId, final int cellX, final int cellY, final boolean notify) {
1531 item.container = container;
1532 item.cellX = cellX;
1533 item.cellY = cellY;
1534 // We store hotseat items in canonical form which is this orientation invariant position
1535 // in the hotseat
1536 if (context instanceof Launcher && screenId < 0 &&
1537 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1538 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
1539 } else {
1540 item.screenId = screenId;
1541 }
1542
1543 final ContentValues values = new ContentValues();
1544 final ContentResolver cr = context.getContentResolver();
1545 item.onAddToDatabase(context, values);
1546
1547 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
1548 values.put(LauncherSettings.Favorites._ID, item.id);
1549 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
1550
1551 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1552 Runnable r = new Runnable() {
1553 public void run() {
1554 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1555 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1556
1557 // Lock on mBgLock *after* the db operation
1558 synchronized (sBgLock) {
1559 checkItemInfoLocked(item.id, item, stackTrace);
1560 sBgItemsIdMap.put(item.id, item);
1561 switch (item.itemType) {
1562 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1563 sBgFolders.put(item.id, (FolderInfo) item);
1564 // Fall through
1565 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1566 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1567 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
1568 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1569 sBgWorkspaceItems.add(item);
1570 } else {
1571 if (!sBgFolders.containsKey(item.container)) {
1572 // Adding an item to a folder that doesn't exist.
1573 String msg = "adding item: " + item + " to a folder that " +
1574 " doesn't exist";
1575 Log.e(TAG, msg);
1576 }
1577 }
1578 break;
1579 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1580 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
1581 break;
1582 }
1583 }
1584 }
1585 };
1586 runOnWorkerThread(r);
1587 }
1588
1589 /**
1590 * Creates a new unique child id, for a given cell span across all layouts.
1591 */
1592 static int getCellLayoutChildId(
1593 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
1594 return (((int) container & 0xFF) << 24)
1595 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
1596 }
1597
1598 private static ArrayList<ItemInfo> getItemsByPackageName(
1599 final String pn, final UserHandleCompat user) {
1600 ItemInfoFilter filter = new ItemInfoFilter() {
1601 @Override
1602 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
1603 return cn.getPackageName().equals(pn) && info.user.equals(user);
1604 }
1605 };
1606 return filterItemInfos(sBgItemsIdMap.values(), filter);
1607 }
1608
1609 /**
1610 * Removes all the items from the database corresponding to the specified package.
1611 */
1612 static void deletePackageFromDatabase(Context context, final String pn,
1613 final UserHandleCompat user) {
1614 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
1615 }
1616
1617 /**
1618 * Removes the specified item from the database
1619 * @param context
1620 * @param item
1621 */
1622 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
1623 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1624 items.add(item);
1625 deleteItemsFromDatabase(context, items);
1626 }
1627
1628 /**
1629 * Removes the specified items from the database
1630 * @param context
1631 * @param item
1632 */
1633 static void deleteItemsFromDatabase(Context context, final ArrayList<ItemInfo> items) {
1634 final ContentResolver cr = context.getContentResolver();
1635
1636 Runnable r = new Runnable() {
1637 public void run() {
1638 for (ItemInfo item : items) {
1639 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
1640 cr.delete(uri, null, null);
1641
1642 // Lock on mBgLock *after* the db operation
1643 synchronized (sBgLock) {
1644 switch (item.itemType) {
1645 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1646 sBgFolders.remove(item.id);
1647 for (ItemInfo info: sBgItemsIdMap.values()) {
1648 if (info.container == item.id) {
1649 // We are deleting a folder which still contains items that
1650 // think they are contained by that folder.
1651 String msg = "deleting a folder (" + item + ") which still " +
1652 "contains items (" + info + ")";
1653 Log.e(TAG, msg);
1654 }
1655 }
1656 sBgWorkspaceItems.remove(item);
1657 break;
1658 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1659 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1660 sBgWorkspaceItems.remove(item);
1661 break;
1662 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1663 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
1664 break;
1665 }
1666 sBgItemsIdMap.remove(item.id);
1667 sBgDbIconCache.remove(item);
1668 }
1669 }
1670 }
1671 };
1672 runOnWorkerThread(r);
1673 }
1674
1675 /**
1676 * Update the order of the workspace screens in the database. The array list contains
1677 * a list of screen ids in the order that they should appear.
1678 */
1679 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
1680 // Log to disk
1681 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
1682 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
1683
1684 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
1685 final ContentResolver cr = context.getContentResolver();
1686 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1687
1688 // Remove any negative screen ids -- these aren't persisted
1689 Iterator<Long> iter = screensCopy.iterator();
1690 while (iter.hasNext()) {
1691 long id = iter.next();
1692 if (id < 0) {
1693 iter.remove();
1694 }
1695 }
1696
1697 Runnable r = new Runnable() {
1698 @Override
1699 public void run() {
1700 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1701 // Clear the table
1702 ops.add(ContentProviderOperation.newDelete(uri).build());
1703 int count = screensCopy.size();
1704 for (int i = 0; i < count; i++) {
1705 ContentValues v = new ContentValues();
1706 long screenId = screensCopy.get(i);
1707 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1708 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1709 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
1710 }
1711
1712 try {
1713 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1714 } catch (Exception ex) {
1715 throw new RuntimeException(ex);
1716 }
1717
1718 synchronized (sBgLock) {
1719 sBgWorkspaceScreens.clear();
1720 sBgWorkspaceScreens.addAll(screensCopy);
1721 }
1722 }
1723 };
1724 runOnWorkerThread(r);
1725 }
1726
1727 /**
1728 * Remove the contents of the specified folder from the database
1729 */
1730 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
1731 final ContentResolver cr = context.getContentResolver();
1732
1733 Runnable r = new Runnable() {
1734 public void run() {
1735 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1736 // Lock on mBgLock *after* the db operation
1737 synchronized (sBgLock) {
1738 sBgItemsIdMap.remove(info.id);
1739 sBgFolders.remove(info.id);
1740 sBgDbIconCache.remove(info);
1741 sBgWorkspaceItems.remove(info);
1742 }
1743
1744 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
1745 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1746 // Lock on mBgLock *after* the db operation
1747 synchronized (sBgLock) {
1748 for (ItemInfo childInfo : info.contents) {
1749 sBgItemsIdMap.remove(childInfo.id);
1750 sBgDbIconCache.remove(childInfo);
1751 }
1752 }
1753 }
1754 };
1755 runOnWorkerThread(r);
1756 }
1757
1758 /**
1759 * Set this as the current Launcher activity object for the loader.
1760 */
1761 public void initialize(Callbacks callbacks) {
1762 synchronized (mLock) {
1763 mCallbacks = new WeakReference<Callbacks>(callbacks);
1764 }
1765 }
1766
1767 @Override
1768 public void onPackageChanged(String packageName, UserHandleCompat user) {
1769 int op = PackageUpdatedTask.OP_UPDATE;
1770 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1771 user));
1772 }
1773
1774 @Override
1775 public void onPackageRemoved(String packageName, UserHandleCompat user) {
1776 int op = PackageUpdatedTask.OP_REMOVE;
1777 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1778 user));
1779 }
1780
1781 @Override
1782 public void onPackageAdded(String packageName, UserHandleCompat user) {
1783 int op = PackageUpdatedTask.OP_ADD;
1784 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1785 user));
1786 }
1787
1788 @Override
1789 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
1790 boolean replacing) {
1791 if (!replacing) {
1792 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
1793 user));
1794 if (mAppsCanBeOnRemoveableStorage) {
1795 // Only rebind if we support removable storage. It catches the
1796 // case where
1797 // apps on the external sd card need to be reloaded
1798 startLoaderFromBackground();
1799 }
1800 } else {
1801 // If we are replacing then just update the packages in the list
1802 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
1803 packageNames, user));
1804 }
1805 }
1806
1807 @Override
1808 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
1809 boolean replacing) {
1810 if (!replacing) {
1811 enqueuePackageUpdated(new PackageUpdatedTask(
1812 PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
1813 user));
1814 }
1815
1816 }
1817
1818 /**
1819 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1820 * ACTION_PACKAGE_CHANGED.
1821 */
1822 @Override
1823 public void onReceive(Context context, Intent intent) {
1824 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
1825
1826 final String action = intent.getAction();
1827 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
1828 // If we have changed locale we need to clear out the labels in all apps/workspace.
1829 forceReload();
1830 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1831 // Check if configuration change was an mcc/mnc change which would affect app resources
1832 // and we would need to clear out the labels in all apps/workspace. Same handling as
1833 // above for ACTION_LOCALE_CHANGED
1834 Configuration currentConfig = context.getResources().getConfiguration();
1835 if (mPreviousConfigMcc != currentConfig.mcc) {
1836 Log.d(TAG, "Reload apps on config change. curr_mcc:"
1837 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
1838 forceReload();
1839 }
1840 // Update previousConfig
1841 mPreviousConfigMcc = currentConfig.mcc;
1842 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
1843 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
1844 if (mCallbacks != null) {
1845 Callbacks callbacks = mCallbacks.get();
1846 if (callbacks != null) {
1847 callbacks.bindSearchablesChanged();
1848 }
1849 }
1850 }
1851 }
1852
1853 void forceReload() {
1854 resetLoadedState(true, true);
1855
1856 // Do this here because if the launcher activity is running it will be restarted.
1857 // If it's not running startLoaderFromBackground will merely tell it that it needs
1858 // to reload.
1859 startLoaderFromBackground();
1860 }
1861
1862 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1863 synchronized (mLock) {
1864 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1865 // mWorkspaceLoaded to true later
1866 stopLoaderLocked();
1867 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1868 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1869 }
1870 }
1871
1872 /**
1873 * When the launcher is in the background, it's possible for it to miss paired
1874 * configuration changes. So whenever we trigger the loader from the background
1875 * tell the launcher that it needs to re-run the loader when it comes back instead
1876 * of doing it now.
1877 */
1878 public void startLoaderFromBackground() {
1879 boolean runLoader = false;
1880 if (mCallbacks != null) {
1881 Callbacks callbacks = mCallbacks.get();
1882 if (callbacks != null) {
1883 // Only actually run the loader if they're not paused.
1884 if (!callbacks.setLoadOnResume()) {
1885 runLoader = true;
1886 }
1887 }
1888 }
1889 if (runLoader) {
1890 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
1891 }
1892 }
1893
1894 // If there is already a loader task running, tell it to stop.
1895 // returns true if isLaunching() was true on the old task
1896 private boolean stopLoaderLocked() {
1897 boolean isLaunching = false;
1898 LoaderTask oldTask = mLoaderTask;
1899 if (oldTask != null) {
1900 if (oldTask.isLaunching()) {
1901 isLaunching = true;
1902 }
1903 oldTask.stopLocked();
1904 }
1905 return isLaunching;
1906 }
1907
1908 public boolean isCurrentCallbacks(Callbacks callbacks) {
1909 return (mCallbacks != null && mCallbacks.get() == callbacks);
1910 }
1911
1912 public void startLoader(boolean isLaunching, int synchronousBindPage) {
1913 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1914 }
1915
1916 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
1917 synchronized (mLock) {
1918 if (DEBUG_LOADERS) {
1919 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1920 }
1921
1922 // Clear any deferred bind-runnables from the synchronized load process
1923 // We must do this before any loading/binding is scheduled below.
1924 synchronized (mDeferredBindRunnables) {
1925 mDeferredBindRunnables.clear();
1926 }
1927
1928 // Don't bother to start the thread if we know it's not going to do anything
1929 if (mCallbacks != null && mCallbacks.get() != null) {
1930 // If there is already one running, tell it to stop.
1931 // also, don't downgrade isLaunching if we're already running
1932 isLaunching = isLaunching || stopLoaderLocked();
1933 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
1934 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
1935 && mAllAppsLoaded && mWorkspaceLoaded) {
1936 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1937 } else {
1938 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1939 sWorker.post(mLoaderTask);
1940 }
1941 }
1942 }
1943 }
1944
1945 void bindRemainingSynchronousPages() {
1946 // Post the remaining side pages to be loaded
1947 if (!mDeferredBindRunnables.isEmpty()) {
1948 Runnable[] deferredBindRunnables = null;
1949 synchronized (mDeferredBindRunnables) {
1950 deferredBindRunnables = mDeferredBindRunnables.toArray(
1951 new Runnable[mDeferredBindRunnables.size()]);
1952 mDeferredBindRunnables.clear();
1953 }
1954 for (final Runnable r : deferredBindRunnables) {
1955 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
1956 }
1957 }
1958 }
1959
1960 public void stopLoader() {
1961 synchronized (mLock) {
1962 if (mLoaderTask != null) {
1963 mLoaderTask.stopLocked();
1964 }
1965 }
1966 }
1967
1968 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1969 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1970 final ContentResolver contentResolver = context.getContentResolver();
1971 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1972 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1973 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1974
1975 try {
1976 final int idIndex = sc.getColumnIndexOrThrow(
1977 LauncherSettings.WorkspaceScreens._ID);
1978 final int rankIndex = sc.getColumnIndexOrThrow(
1979 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1980 while (sc.moveToNext()) {
1981 try {
1982 long screenId = sc.getLong(idIndex);
1983 int rank = sc.getInt(rankIndex);
1984 orderedScreens.put(rank, screenId);
1985 } catch (Exception e) {
1986 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e,🔵
1987 }
1988 }
1989 } finally {
1990 sc.close();
1991 }
1992
1993 // Log to disk
1994 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1995 ArrayList<String> orderedScreensPairs= new ArrayList<String>();
1996 for (Integer i : orderedScreens.keySet()) {
1997 orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
1998 }
1999 Launcher.addDumpLog(TAG, "11683562 - screens: " +
2000 TextUtils.join(", ", orderedScreensPairs), true);
2001 return orderedScreens;
2002 }
2003
2004 public boolean isAllAppsLoaded() {
2005 return mAllAppsLoaded;
2006 }
2007
2008 boolean isLoadingWorkspace() {
2009 synchronized (mLock) {
2010 if (mLoaderTask != null) {
2011 return mLoaderTask.isLoadingWorkspace();
2012 }
2013 }
2014 return false;
2015 }
2016
2017 /**
2018 * Runnable for the thread that loads the contents of the launcher:
2019 * - workspace icons
2020 * - widgets
2021 * - all apps icons
2022 */
2023 private class LoaderTask implements Runnable {
2024 private Context mContext;
2025 private boolean mIsLaunching;
2026 private boolean mIsLoadingAndBindingWorkspace;
2027 private boolean mStopped;
2028 private boolean mLoadAndBindStepFinished;
2029 private int mFlags;
2030
2031 private HashMap<Object, CharSequence> mLabelCache;
2032
2033 LoaderTask(Context context, boolean isLaunching, int flags) {
2034 mContext = context;
2035 mIsLaunching = isLaunching;
2036 mLabelCache = new HashMap<Object, CharSequence>();
2037 mFlags = flags;
2038 }
2039
2040 boolean isLaunching() {
2041 return mIsLaunching;
2042 }
2043
2044 boolean isLoadingWorkspace() {
2045 return mIsLoadingAndBindingWorkspace;
2046 }
2047
2048 /** Returns whether this is an upgrade path */
2049 private boolean loadAndBindWorkspace() {
2050 mIsLoadingAndBindingWorkspace = true;
2051
2052 // Load the workspace
2053 if (DEBUG_LOADERS) {
2054 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
2055 }
2056
2057 boolean isUpgradePath = false;
2058 if (!mWorkspaceLoaded) {
2059 isUpgradePath = loadWorkspace();
2060 synchronized (LoaderTask.this) {
2061 if (mStopped) {
2062 return isUpgradePath;
2063 }
2064 mWorkspaceLoaded = true;
2065 }
2066 }
2067
2068 // Bind the workspace
2069 bindWorkspace(-1, isUpgradePath);
2070 return isUpgradePath;
2071 }
2072
2073 private void waitForIdle() {
2074 // Wait until the either we're stopped or the other threads are done.
2075 // This way we don't start loading all apps until the workspace has settled
2076 // down.
2077 synchronized (LoaderTask.this) {
2078 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2079
2080 mHandler.postIdle(new Runnable() {
2081 public void run() {
2082 synchronized (LoaderTask.this) {
2083 mLoadAndBindStepFinished = true;
2084 if (DEBUG_LOADERS) {
2085 Log.d(TAG, "done with previous binding step");
2086 }
2087 LoaderTask.this.notify();
2088 }
2089 }
2090 });
2091
2092 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
2093 try {
2094 // Just in case mFlushingWorkerThread changes but we aren't woken up,
2095 // wait no longer than 1sec at a time
2096 this.wait(1000);
2097 } catch (InterruptedException ex) {
2098 // Ignore
2099 }
2100 }
2101 if (DEBUG_LOADERS) {
2102 Log.d(TAG, "waited "
2103 + (SystemClock.uptimeMillis()-workspaceWaitTime)
2104 + "ms for previous step to finish binding");
2105 }
2106 }
2107 }
2108
2109 void runBindSynchronousPage(int synchronousBindPage) {
2110 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
2111 // Ensure that we have a valid page index to load synchronously
2112 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
2113 "valid page index");
2114 }
2115 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
2116 // Ensure that we don't try and bind a specified page when the pages have not been
2117 // loaded already (we should load everything asynchronously in that case)
2118 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
2119 }
2120 synchronized (mLock) {
2121 if (mIsLoaderTaskRunning) {
2122 // Ensure that we are never running the background loading at this point since
2123 // we also touch the background collections
2124 throw new RuntimeException("Error! Background loading is already running");
2125 }
2126 }
2127
2128 // XXX: Throw an exception if we are already loading (since we touch the worker thread
2129 // data structures, we can't allow any other thread to touch that data, but because
2130 // this call is synchronous, we can get away with not locking).
2131
2132 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
2133 // operations from the previous activity. We need to ensure that all queued operations
2134 // are executed before any synchronous binding work is done.
2135 mHandler.flush();
2136
2137 // Divide the set of loaded items into those that we are binding synchronously, and
2138 // everything else that is to be bound normally (asynchronously).
2139 bindWorkspace(synchronousBindPage, false);
2140 // XXX: For now, continue posting the binding of AllApps as there are other issues that
2141 // arise from that.
2142 onlyBindAllApps();
2143 }
2144
2145 public void run() {
2146 boolean isUpgrade = false;
2147
2148 synchronized (mLock) {
2149 mIsLoaderTaskRunning = true;
2150 }
2151 // Optimize for end-user experience: if the Launcher is up and // running with the
2152 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
2153 // workspace first (default).
2154 keep_running: {
2155 // Elevate priority when Home launches for the first time to avoid
2156 // starving at boot time. Staring at a blank home is not cool.
2157 synchronized (mLock) {
2158 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
2159 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
2160 android.os.Process.setThreadPriority(mIsLaunching
2161 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
2162 }
2163 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
2164 isUpgrade = loadAndBindWorkspace();
2165
2166 if (mStopped) {
2167 break keep_running;
2168 }
2169
2170 // Whew! Hard work done. Slow us down, and wait until the UI thread has
2171 // settled down.
2172 synchronized (mLock) {
2173 if (mIsLaunching) {
2174 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
2175 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
2176 }
2177 }
2178 waitForIdle();
2179
2180 // second step
2181 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
2182 loadAndBindAllApps();
2183
2184 // Restore the default thread priority after we are done loading items
2185 synchronized (mLock) {
2186 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
2187 }
2188 }
2189
2190 // Update the saved icons if necessary
2191 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
2192 synchronized (sBgLock) {
2193 for (Object key : sBgDbIconCache.keySet()) {
2194 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
2195 }
2196 sBgDbIconCache.clear();
2197 }
2198
2199 if (LauncherAppState.isDisableAllApps()) {
2200 // Ensure that all the applications that are in the system are
2201 // represented on the home screen.
2202 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
2203 verifyApplications();
2204 }
2205 }
2206
2207 // Clear out this reference, otherwise we end up holding it until all of the
2208 // callback runnables are done.
2209 mContext = null;
2210
2211 synchronized (mLock) {
2212 // If we are still the last one to be scheduled, remove ourselves.
2213 if (mLoaderTask == this) {
2214 mLoaderTask = null;
2215 }
2216 mIsLoaderTaskRunning = false;
2217 }
2218 }
2219
2220 public void stopLocked() {
2221 synchronized (LoaderTask.this) {
2222 mStopped = true;
2223 this.notify();
2224 }
2225 }
2226
2227 /**
2228 * Gets the callbacks object. If we've been stopped, or if the launcher object
2229 * has somehow been garbage collected, return null instead. Pass in the Callbacks
2230 * object that was around when the deferred message was scheduled, and if there's
2231 * a new Callbacks object around then also return null. This will save us from
2232 * calling onto it with data that will be ignored.
2233 */
2234 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
2235 synchronized (mLock) {
2236 if (mStopped) {
2237 return null;
2238 }
2239
2240 if (mCallbacks == null) {
2241 return null;
2242 }
2243
2244 final Callbacks callbacks = mCallbacks.get();
2245 if (callbacks != oldCallbacks) {
2246 return null;
2247 }
2248 if (callbacks == null) {
2249 Log.w(TAG, "no mCallbacks");
2250 return null;
2251 }
2252
2253 return callbacks;
2254 }
2255 }
2256
2257 private void verifyApplications() {
2258 final Context context = mApp.getContext();
2259
2260 // Cross reference all the applications in our apps list with items in the workspace
2261 ArrayList<ItemInfo> tmpInfos;
2262 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
2263 synchronized (sBgLock) {
2264 for (AppInfo app : mBgAllAppsList.data) {
2265 tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
2266 if (tmpInfos.isEmpty()) {
2267 // We are missing an application icon, so add this to the workspace
2268 added.add(app);
2269 // This is a rare event, so lets log it
2270 Log.e(TAG, "Missing Application on load: " + app);
2271 }
2272 }
2273 }
2274 if (!added.isEmpty()) {
2275 addAndBindAddedWorkspaceApps(context, added);
2276 }
2277 }
2278
2279 // check & update map of what's occupied; used to discard overlapping/invalid items
2280 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item,
2281 AtomicBoolean deleteOnInvalidPlacement) {
2282 LauncherAppState app = LauncherAppState.getInstance();
2283 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2284 final int countX = (int) grid.numColumns;
2285 final int countY = (int) grid.numRows;
2286
2287 long containerIndex = item.screenId;
2288 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2289 // Return early if we detect that an item is under the hotseat button
2290 if (mCallbacks == null ||
2291 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
2292 deleteOnInvalidPlacement.set(true);
2293 Log.e(TAG, "Error loading shortcut into hotseat " + item
2294 + " into position (" + item.screenId + ":" + item.cellX + ","
2295 + item.cellY + ") occupied by all apps");
2296 return false;
2297 }
2298
2299 final ItemInfo[][] hotseatItems =
2300 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
2301
2302 if (item.screenId >= grid.numHotseatIcons) {
2303 Log.e(TAG, "Error loading shortcut " + item
2304 + " into hotseat position " + item.screenId
2305 + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
2306 + ")");
2307 return false;
2308 }
2309
2310 if (hotseatItems != null) {
2311 if (hotseatItems[(int) item.screenId][0] != null) {
2312 Log.e(TAG, "Error loading shortcut into hotseat " + item
2313 + " into position (" + item.screenId + ":" + item.cellX + ","
2314 + item.cellY + ") occupied by "
2315 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
2316 [(int) item.screenId][0]);
2317 return false;
2318 } else {
2319 hotseatItems[(int) item.screenId][0] = item;
2320 return true;
2321 }
2322 } else {
2323 final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
2324 items[(int) item.screenId][0] = item;
2325 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
2326 return true;
2327 }
2328 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
2329 // Skip further checking if it is not the hotseat or workspace container
2330 return true;
2331 }
2332
2333 if (!occupied.containsKey(item.screenId)) {
2334 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
2335 occupied.put(item.screenId, items);
2336 }
2337
2338 final ItemInfo[][] screens = occupied.get(item.screenId);
2339 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2340 item.cellX < 0 || item.cellY < 0 ||
2341 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
2342 Log.e(TAG, "Error loading shortcut " + item
2343 + " into cell (" + containerIndex + "-" + item.screenId + ":"
2344 + item.cellX + "," + item.cellY
2345 + ") out of screen bounds ( " + countX + "x" + countY + ")");
2346 return false;
2347 }
2348
2349 // Check if any workspace icons overlap with each other
2350 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
2351 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
2352 if (screens[x][y] != null) {
2353 Log.e(TAG, "Error loading shortcut " + item
2354 + " into cell (" + containerIndex + "-" + item.screenId + ":"
2355 + x + "," + y
2356 + ") occupied by "
2357 + screens[x][y]);
2358 return false;
2359 }
2360 }
2361 }
2362 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
2363 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
2364 screens[x][y] = item;
2365 }
2366 }
2367
2368 return true;
2369 }
2370
2371 /** Clears all the sBg data structures */
2372 private void clearSBgDataStructures() {
2373 synchronized (sBgLock) {
2374 sBgWorkspaceItems.clear();
2375 sBgAppWidgets.clear();
2376 sBgFolders.clear();
2377 sBgItemsIdMap.clear();
2378 sBgDbIconCache.clear();
2379 sBgWorkspaceScreens.clear();
2380 }
2381 }
2382
2383 /** Returns whether this is an upgrade path */
2384 private boolean loadWorkspace() {
2385 // Log to disk
2386 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
2387
2388 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2389
2390 final Context context = mContext;
2391 final ContentResolver contentResolver = context.getContentResolver();
2392 final PackageManager manager = context.getPackageManager();
2393 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
2394 final boolean isSafeMode = manager.isSafeMode();
2395 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2396 final boolean isSdCardReady = context.registerReceiver(null,
2397 new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
2398
2399 LauncherAppState app = LauncherAppState.getInstance();
2400 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2401 int countX = (int) grid.numColumns;
2402 int countY = (int) grid.numRows;
2403
2404 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
2405 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
2406 LauncherAppState.getLauncherProvider().deleteDatabase();
2407 }
2408
2409 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
2410 // append the user's Launcher2 shortcuts
2411 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
2412 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
2413 } else {
2414 // Make sure the default workspace is loaded
2415 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
2416 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
2417 }
2418
2419 // This code path is for our old migration code and should no longer be exercised
2420 boolean loadedOldDb = false;
2421
2422 // Log to disk
2423 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
2424
2425 synchronized (sBgLock) {
2426 clearSBgDataStructures();
2427 final HashSet<String> installingPkgs = PackageInstallerCompat
2428 .getInstance(mContext).updateAndGetActiveSessionCache();
2429
2430 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
2431 final ArrayList<Long> restoredRows = new ArrayList<Long>();
2432 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
2433 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
2434 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
2435
2436 // +1 for the hotseat (it can be larger than the workspace)
2437 // Load workspace in reverse order to ensure that latest items are loaded first (and
2438 // before any earlier duplicates)
2439 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
2440
2441 try {
2442 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
2443 final int intentIndex = c.getColumnIndexOrThrow
2444 (LauncherSettings.Favorites.INTENT);
2445 final int titleIndex = c.getColumnIndexOrThrow
2446 (LauncherSettings.Favorites.TITLE);
2447 final int iconTypeIndex = c.getColumnIndexOrThrow(
2448 LauncherSettings.Favorites.ICON_TYPE);
2449 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
2450 final int iconPackageIndex = c.getColumnIndexOrThrow(
2451 LauncherSettings.Favorites.ICON_PACKAGE);
2452 final int iconResourceIndex = c.getColumnIndexOrThrow(
2453 LauncherSettings.Favorites.ICON_RESOURCE);
2454 final int containerIndex = c.getColumnIndexOrThrow(
2455 LauncherSettings.Favorites.CONTAINER);
2456 final int itemTypeIndex = c.getColumnIndexOrThrow(
2457 LauncherSettings.Favorites.ITEM_TYPE);
2458 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
2459 LauncherSettings.Favorites.APPWIDGET_ID);
2460 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
2461 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
2462 final int screenIndex = c.getColumnIndexOrThrow(
2463 LauncherSettings.Favorites.SCREEN);
2464 final int cellXIndex = c.getColumnIndexOrThrow
2465 (LauncherSettings.Favorites.CELLX);
2466 final int cellYIndex = c.getColumnIndexOrThrow
2467 (LauncherSettings.Favorites.CELLY);
2468 final int spanXIndex = c.getColumnIndexOrThrow
2469 (LauncherSettings.Favorites.SPANX);
2470 final int spanYIndex = c.getColumnIndexOrThrow(
2471 LauncherSettings.Favorites.SPANY);
2472 final int restoredIndex = c.getColumnIndexOrThrow(
2473 LauncherSettings.Favorites.RESTORED);
2474 final int profileIdIndex = c.getColumnIndexOrThrow(
2475 LauncherSettings.Favorites.PROFILE_ID);
2476 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
2477 //final int displayModeIndex = c.getColumnIndexOrThrow(
2478 // LauncherSettings.Favorites.DISPLAY_MODE);
2479
2480 ShortcutInfo info;
2481 String intentDescription;
2482 LauncherAppWidgetInfo appWidgetInfo;
2483 int container;
2484 long id;
2485 Intent intent;
2486 UserHandleCompat user;
2487
2488 while (!mStopped && c.moveToNext()) {
2489 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
2490 try {
2491 int itemType = c.getInt(itemTypeIndex);
2492 boolean restored = 0 != c.getInt(restoredIndex);
2493 boolean allowMissingTarget = false;
2494
2495 switch (itemType) {
2496 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2497 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2498 id = c.getLong(idIndex);
2499 intentDescription = c.getString(intentIndex);
2500 long serialNumber = c.getInt(profileIdIndex);
2501 user = mUserManager.getUserForSerialNumber(serialNumber);
2502 int promiseType = c.getInt(restoredIndex);
2503 if (user == null) {
2504 // User has been deleted remove the item.
2505 itemsToRemove.add(id);
2506 continue;
2507 }
2508 try {
2509 intent = Intent.parseUri(intentDescription, 0);
2510 ComponentName cn = intent.getComponent();
2511 if (cn != null && cn.getPackageName() != null) {
2512 boolean validPkg = launcherApps.isPackageEnabledForProfile(
2513 cn.getPackageName(), user);
2514 boolean validComponent = validPkg &&
2515 launcherApps.isActivityEnabledForProfile(cn, user);
2516
2517 if (validComponent) {
2518 if (restored) {
2519 // no special handling necessary for this item
2520 restoredRows.add(id);
2521 restored = false;
2522 }
2523 } else if (validPkg) {
2524 intent = null;
2525 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
2526 // We allow auto install apps to have their intent
2527 // updated after an install.
2528 intent = manager.getLaunchIntentForPackage(
2529 cn.getPackageName());
2530 if (intent != null) {
2531 ContentValues values = new ContentValues();
2532 values.put(LauncherSettings.Favorites.INTENT,
2533 intent.toUri(0));
2534 String where = BaseColumns._ID + "= ?";
2535 String[] args = {Long.toString(id)};
2536 contentResolver.update(contentUri, values, where, arg🔵
2537 }
2538 }
2539
2540 if (intent == null) {
2541 // The app is installed but the component is no
2542 // longer available.
2543 Launcher.addDumpLog(TAG,
2544 "Invalid component removed: " + cn, true);
2545 itemsToRemove.add(id);
2546 continue;
2547 } else {
2548 // no special handling necessary for this item
2549 restoredRows.add(id);
2550 restored = false;
2551 }
2552 } else if (restored) {
2553 // Package is not yet available but might be
2554 // installed later.
2555 Launcher.addDumpLog(TAG,
2556 "package not yet restored: " + cn, true);
2557
2558 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
2559 // Restore has started once.
2560 } else if (installingPkgs.contains(cn.getPackageName())) {
2561 // App restore has started. Update the flag
2562 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
2563 ContentValues values = new ContentValues();
2564 values.put(LauncherSettings.Favorites.RESTORED,
2565 promiseType);
2566 String where = BaseColumns._ID + "= ?";
2567 String[] args = {Long.toString(id)};
2568 contentResolver.update(contentUri, values, where, args);
2569
2570 } else if (REMOVE_UNRESTORED_ICONS) {
2571 Launcher.addDumpLog(TAG,
2572 "Unrestored package removed: " + cn, true);
2573 itemsToRemove.add(id);
2574 continue;
2575 }
2576 } else if (isSdCardReady) {
2577 // Do not wait for external media load anymore.
2578 // Log the invalid package, and remove it
2579 Launcher.addDumpLog(TAG,
2580 "Invalid package removed: " + cn, true);
2581 itemsToRemove.add(id);
2582 continue;
2583 } else {
2584 // SdCard is not ready yet. Package might get available,
2585 // once it is ready.
2586 Launcher.addDumpLog(TAG, "Invalid package: " + cn
2587 + " (check again later)", true);
2588 HashSet<String> pkgs = sPendingPackages.get(user);
2589 if (pkgs == null) {
2590 pkgs = new HashSet<String>();
2591 sPendingPackages.put(user, pkgs);
2592 }
2593 pkgs.add(cn.getPackageName());
2594 allowMissingTarget = true;
2595 // Add the icon on the workspace anyway.
2596 }
2597 } else if (cn == null) {
2598 // For shortcuts with no component, keep them as they are
2599 restoredRows.add(id);
2600 restored = false;
2601 }
2602 } catch (URISyntaxException e) {
2603 Launcher.addDumpLog(TAG,
2604 "Invalid uri: " + intentDescription, true);
2605 continue;
2606 }
2607
2608 if (restored) {
2609 <<<<<<< GitAnalyzerPlus_ours
2610 if (user.equals(UserHandleCompat.myUserHandle())) {
2611 Launcher.addDumpLog(TAG,
2612 "constructing info for partially restored package",
2613 true);
2614 info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
2615 intent = getRestoredItemIntent(c, context, intent);
2616 } else {
2617 // Don't restore items for other profiles.
2618 itemsToRemove.add(id);
2619 continue;
2620 }
2621 ||||||| GitAnalyzerPlus_base
2622 LauncherSettings.Favorites.CONTAINER);
2623 final int itemTypeIndex = c.getColumnIndexOrThrow(
2624 LauncherSettings.Favorites.ITEM_TYPE);
2625 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
2626 LauncherSettings.Favorites.APPWIDGET_ID);
2627 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
2628 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
2629 final int screenIndex = c.getColumnIndexOrThrow(
2630 LauncherSettings.Favorites.SCREEN);
2631 final int cellXIndex = c.getColumnIndexOrThrow
2632 (LauncherSettings.Favorites.CELLX);
2633 final int cellYIndex = c.getColumnIndexOrThrow
2634 (LauncherSettings.Favorites.CELLY);
2635 final int spanXIndex = c.getColumnIndexOrThrow
2636 (LauncherSettings.Favorites.SPANX);
2637 final int spanYIndex = c.getColumnIndexOrThrow(
2638 LauncherSettings.Favorites.SPANY);
2639 final int restoredIndex = c.getColumnIndexOrThrow(
2640 LauncherSettings.Favorites.RESTORED);
2641 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
2642 //final int displayModeIndex = c.getColumnIndexOrThrow(
2643 // LauncherSettings.Favorites.DISPLAY_MODE);
2644
2645 ShortcutInfo info;
2646 String intentDescription;
2647 LauncherAppWidgetInfo appWidgetInfo;
2648 int container;
2649 long id;
2650 Intent intent;
2651
2652 while (!mStopped && c.moveToNext()) {
2653 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
2654 try {
2655 int itemType = c.getInt(itemTypeIndex);
2656 boolean restored = 0 != c.getInt(restoredIndex);
2657
2658 switch (itemType) {
2659 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2660 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2661 id = c.getLong(idIndex);
2662 intentDescription = c.getString(intentIndex);
2663 try {
2664 intent = Intent.parseUri(intentDescription, 0);
2665 ComponentName cn = intent.getComponent();
2666 if (cn != null && !isValidPackageComponent(manager, cn)) {
2667 if (restored) {
2668 // might be installed later
2669 Launcher.addDumpLog(TAG,
2670 "package not yet restored: " + cn, true);
2671 } else {
2672 if (!mAppsCanBeOnRemoveableStorage) {
2673 // Log the invalid package, and remove it
2674 Launcher.addDumpLog(TAG,
2675 "Invalid package removed: " + cn, true);
2676 itemsToRemove.add(id);
2677 } else {
2678 // If apps can be on external storage, then we just
2679 // leave them for the user to remove (maybe add
2680 // visual treatment to it)
2681 Launcher.addDumpLog(TAG,
2682 "Invalid package found: " + cn, true);
2683 }
2684 continue;
2685 }
2686 } else if (restored) {
2687 // no special handling necessary for this restored item
2688 restoredRows.add(id);
2689 restored = false;
2690 }
2691 } catch (URISyntaxException e) {
2692 Launcher.addDumpLog(TAG,
2693 "Invalid uri: " + intentDescription, true);
2694 continue;
2695 }
2696
2697 if (restored) {
2698 Launcher.addDumpLog(TAG,
2699 "constructing info for partially restored package",
2700 true);
2701 info = getRestoredItemInfo(c, titleIndex, intent);
2702 intent = getRestoredItemIntent(c, context, intent);
2703 } else if (itemType ==
2704 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
2705 info = getShortcutInfo(manager, intent, context, c, iconIndex,
2706 titleIndex, mLabelCache);
2707 } else {
2708 info = getShortcutInfo(c, context, iconTypeIndex,
2709 iconPackageIndex, iconResourceIndex, iconIndex,
2710 titleIndex);
2711
2712 // App shortcuts that used to be automatically added to Launcher
2713 // didn't always have the correct intent flags set, so do that
2714 // here
2715 if (intent.getAction() != null &&
2716 intent.getCategories() != null &&
2717 intent.getAction().equals(Intent.ACTION_MAIN) &&
2718 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
2719 intent.addFlags(
2720 Intent.FLAG_ACTIVITY_NEW_TASK |
2721 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
2722 }
2723 }
2724
2725 if (info != null) {
2726 info.id = id;
2727 info.intent = intent;
2728 container = c.getInt(containerIndex);
2729 info.container = container;
2730 info.screenId = c.getInt(screenIndex);
2731 info.cellX = c.getInt(cellXIndex);
2732 info.cellY = c.getInt(cellYIndex);
2733 info.spanX = 1;
2734 info.spanY = 1;
2735
2736 // check & update map of what's occupied
2737 deleteOnInvalidPlacement.set(false);
2738 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) {
2739 if (deleteOnInvalidPlacement.get()) {
2740 itemsToRemove.add(id);
2741 }
2742 break;
2743 }
2744
2745 switch (container) {
2746 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2747 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2748 sBgWorkspaceItems.add(info);
2749 break;
2750 default:
2751 // Item is in a user folder
2752 FolderInfo folderInfo =
2753 findOrMakeFolder(sBgFolders, container);
2754 folderInfo.add(info);
2755 break;
2756 }
2757 sBgItemsIdMap.put(info.id, info);
2758
2759 // now that we've loaded everthing re-save it with the
2760 // icon in case it disappears somehow.
2761 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
2762 } else {
2763 throw new RuntimeException("Unexpected null ShortcutInfo");
2764 }
2765 break;
2766
2767 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2768 id = c.getLong(idIndex);
2769 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
2770
2771 folderInfo.title = c.getString(titleIndex);
2772 folderInfo.id = id;
2773 container = c.getInt(containerIndex);
2774 folderInfo.container = container;
2775 folderInfo.screenId = c.getInt(screenIndex);
2776 folderInfo.cellX = c.getInt(cellXIndex);
2777 folderInfo.cellY = c.getInt(cellYIndex);
2778 folderInfo.spanX = 1;
2779 folderInfo.spanY = 1;
2780
2781 // check & update map of what's occupied
2782 deleteOnInvalidPlacement.set(false);
2783 if (!checkItemPlacement(occupied, folderInfo,
2784 deleteOnInvalidPlacement)) {
2785 if (deleteOnInvalidPlacement.get()) {
2786 itemsToRemove.add(id);
2787 }
2788 break;
2789 }
2790
2791 switch (container) {
2792 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2793 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2794 sBgWorkspaceItems.add(folderInfo);
2795 break;
2796 }
2797
2798 if (restored) {
2799 // no special handling required for restored folders
2800 restoredRows.add(id);
2801 }
2802
2803 sBgItemsIdMap.put(folderInfo.id, folderInfo);
2804 sBgFolders.put(folderInfo.id, folderInfo);
2805 break;
2806
2807 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2808 // Read all Launcher-specific widget details
2809 int appWidgetId = c.getInt(appWidgetIdIndex);
2810 String savedProvider = c.getString(appWidgetProviderIndex);
2811
2812 id = c.getLong(idIndex);
2813
2814 final AppWidgetProviderInfo provider =
2815 widgets.getAppWidgetInfo(appWidgetId);
2816
2817 if (!isSafeMode && (provider == null || provider.provider == null ||
2818 provider.provider.getPackageName() == null)) {
2819 String log = "Deleting widget that isn't installed anymore: id="
2820 + id + " appWidgetId=" + appWidgetId;
2821 Log.e(TAG, log);
2822 Launcher.addDumpLog(TAG, log, false);
2823 itemsToRemove.add(id);
2824 } else {
2825 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2826 provider.provider);
2827 appWidgetInfo.id = id;
2828 appWidgetInfo.screenId = c.getInt(screenIndex);
2829 appWidgetInfo.cellX = c.getInt(cellXIndex);
2830 appWidgetInfo.cellY = c.getInt(cellYIndex);
2831 appWidgetInfo.spanX = c.getInt(spanXIndex);
2832 appWidgetInfo.spanY = c.getInt(spanYIndex);
2833 int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
2834 appWidgetInfo.minSpanX = minSpan[0];
2835 appWidgetInfo.minSpanY = minSpan[1];
2836
2837 container = c.getInt(containerIndex);
2838 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2839 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2840 Log.e(TAG, "Widget found where container != " +
2841 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
2842 continue;
2843 }
2844
2845 appWidgetInfo.container = c.getInt(containerIndex);
2846 // check & update map of what's occupied
2847 deleteOnInvalidPlacement.set(false);
2848 if (!checkItemPlacement(occupied, appWidgetInfo,
2849 deleteOnInvalidPlacement)) {
2850 if (deleteOnInvalidPlacement.get()) {
2851 itemsToRemove.add(id);
2852 }
2853 break;
2854 }
2855 String providerName = provider.provider.flattenToString();
2856 if (!providerName.equals(savedProvider)) {
2857 ContentValues values = new ContentValues();
2858 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
2859 providerName);
2860 String where = BaseColumns._ID + "= ?";
2861 String[] args = {Integer.toString(c.getInt(idIndex))};
2862 contentResolver.update(contentUri, values, where, args);
2863 }
2864 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2865 sBgAppWidgets.add(appWidgetInfo);
2866 }
2867 break;
2868 }
2869 } catch (Exception e) {
2870 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
2871 }
2872 }
2873 } finally {
2874 if (c != null) {
2875 c.close();
2876 }
2877 }
2878
2879 // Break early if we've stopped loading
2880 if (mStopped) {
2881 clearSBgDataStructures();
2882 return false;
2883 }
2884
2885 if (itemsToRemove.size() > 0) {
2886 ContentProviderClient client = contentResolver.acquireContentProviderClient(
2887 LauncherSettings.Favorites.CONTENT_URI);
2888 // Remove dead items
2889 for (long id : itemsToRemove) {
2890 if (DEBUG_LOADERS) {
2891 Log.d(TAG, "Removed id = " + id);
2892 }
2893 // Don't notify content observers
2894 try {
2895 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
2896 null, null);
2897 } catch (RemoteException e) {
2898 Log.w(TAG, "Could not remove id = " + id);
2899 }
2900 }
2901 }
2902
2903 if (restoredRows.size() > 0) {
2904 ContentProviderClient updater = contentResolver.acquireContentProviderClient(
2905 LauncherSettings.Favorites.CONTENT_URI);
2906 =======
2907 Launcher.addDumpLog(TAG,
2908 "constructing info for partially restored package",
2909 true);
2910 info = getRestoredItemInfo(c, titleIndex);
2911 intent = getRestoredItemIntent(c, context, intent);
2912 >>>>>>> GitAnalyzerPlus_theirs
2913 } else if (itemType ==
2914 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
2915 info = getShortcutInfo(manager, intent, user, context, c,
2916 iconIndex, titleIndex, mLabelCache, allowMissingTarget);
2917 } else {
2918 info = getShortcutInfo(c, context, iconTypeIndex,
2919 iconPackageIndex, iconResourceIndex, iconIndex,
2920 titleIndex);
2921
2922 // App shortcuts that used to be automatically added to Launcher
2923 // didn't always have the correct intent flags set, so do that
2924 // here
2925 if (intent.getAction() != null &&
2926 intent.getCategories() != null &&
2927 intent.getAction().equals(Intent.ACTION_MAIN) &&
2928 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
2929 intent.addFlags(
2930 Intent.FLAG_ACTIVITY_NEW_TASK |
2931 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
2932 }
2933 }
2934
2935 if (info != null) {
2936 info.id = id;
2937 info.intent = intent;
2938 container = c.getInt(containerIndex);
2939 info.container = container;
2940 info.screenId = c.getInt(screenIndex);
2941 info.cellX = c.getInt(cellXIndex);
2942 info.cellY = c.getInt(cellYIndex);
2943 info.spanX = 1;
2944 info.spanY = 1;
2945 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
2946 info.isDisabled = isSafeMode
2947 && !Utilities.isSystemApp(context, intent);
2948
2949 // check & update map of what's occupied
2950 deleteOnInvalidPlacement.set(false);
2951 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) {
2952 if (deleteOnInvalidPlacement.get()) {
2953 itemsToRemove.add(id);
2954 }
2955 break;
2956 }
2957
2958 switch (container) {
2959 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2960 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2961 sBgWorkspaceItems.add(info);
2962 break;
2963 default:
2964 // Item is in a user folder
2965 FolderInfo folderInfo =
2966 findOrMakeFolder(sBgFolders, container);
2967 folderInfo.add(info);
2968 break;
2969 }
2970 sBgItemsIdMap.put(info.id, info);
2971
2972 // now that we've loaded everthing re-save it with the
2973 // icon in case it disappears somehow.
2974 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
2975 } else {
2976 throw new RuntimeException("Unexpected null ShortcutInfo");
2977 }
2978 break;
2979
2980 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2981 id = c.getLong(idIndex);
2982 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
2983
2984 folderInfo.title = c.getString(titleIndex);
2985 folderInfo.id = id;
2986 container = c.getInt(containerIndex);
2987 folderInfo.container = container;
2988 folderInfo.screenId = c.getInt(screenIndex);
2989 folderInfo.cellX = c.getInt(cellXIndex);
2990 folderInfo.cellY = c.getInt(cellYIndex);
2991 folderInfo.spanX = 1;
2992 folderInfo.spanY = 1;
2993
2994 // check & update map of what's occupied
2995 deleteOnInvalidPlacement.set(false);
2996 if (!checkItemPlacement(occupied, folderInfo,
2997 deleteOnInvalidPlacement)) {
2998 if (deleteOnInvalidPlacement.get()) {
2999 itemsToRemove.add(id);
3000 }
3001 break;
3002 }
3003
3004 switch (container) {
3005 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
3006 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
3007 sBgWorkspaceItems.add(folderInfo);
3008 break;
3009 }
3010
3011 if (restored) {
3012 // no special handling required for restored folders
3013 restoredRows.add(id);
3014 }
3015
3016 sBgItemsIdMap.put(folderInfo.id, folderInfo);
3017 sBgFolders.put(folderInfo.id, folderInfo);
3018 break;
3019
3020 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
3021 // Read all Launcher-specific widget details
3022 int appWidgetId = c.getInt(appWidgetIdIndex);
3023 String savedProvider = c.getString(appWidgetProviderIndex);
3024 id = c.getLong(idIndex);
3025 final ComponentName component =
3026 ComponentName.unflattenFromString(savedProvider);
3027
3028 final int restoreStatus = c.getInt(restoredIndex);
3029 final boolean isIdValid = (restoreStatus &
3030 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
3031
3032 final boolean wasProviderReady = (restoreStatus &
3033 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
3034
3035 final AppWidgetProviderInfo provider = isIdValid
3036 ? widgets.getAppWidgetInfo(appWidgetId)
3037 : findAppWidgetProviderInfoWithComponent(context, component);
3038
3039 final boolean isProviderReady = isValidProvider(provider);
3040 if (!isSafeMode && wasProviderReady && !isProviderReady) {
3041 String log = "Deleting widget that isn't installed anymore: "
3042 + "id=" + id + " appWidgetId=" + appWidgetId;
3043 Log.e(TAG, log);
3044 Launcher.addDumpLog(TAG, log, false);
3045 itemsToRemove.add(id);
3046 } else {
3047 if (isProviderReady) {
3048 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
3049 provider.provider);
3050 int[] minSpan =
3051 Launcher.getMinSpanForWidget(context, provider);
3052 appWidgetInfo.minSpanX = minSpan[0];
3053 appWidgetInfo.minSpanY = minSpan[1];
3054
3055 int status = restoreStatus;
3056 if (!wasProviderReady) {
3057 // If provider was not previously ready, update the
3058 // status and UI flag.
3059
3060 // Id would be valid only if the widget restore broadcast was🔵
3061 if (isIdValid) {
3062 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
3063 } else {
3064 status &= ~LauncherAppWidgetInfo
3065 .FLAG_PROVIDER_NOT_READY;
3066 }
3067 }
3068 appWidgetInfo.restoreStatus = status;
3069 } else {
3070 Log.v(TAG, "Widget restore pending id=" + id
3071 + " appWidgetId=" + appWidgetId
3072 + " status =" + restoreStatus);
3073 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
3074 component);
3075 appWidgetInfo.restoreStatus = restoreStatus;
3076
3077 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) 🔵
3078 // Restore has started once.
3079 } else if (installingPkgs.contains(component.getPackageName())) {
3080 // App restore has started. Update the flag
3081 appWidgetInfo.restoreStatus |=
3082 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
3083 } else if (REMOVE_UNRESTORED_ICONS) {
3084 Launcher.addDumpLog(TAG,
3085 "Unrestored widget removed: " + component, true);
3086 itemsToRemove.add(id);
3087 continue;
3088 }
3089 }
3090
3091 appWidgetInfo.id = id;
3092 appWidgetInfo.screenId = c.getInt(screenIndex);
3093 appWidgetInfo.cellX = c.getInt(cellXIndex);
3094 appWidgetInfo.cellY = c.getInt(cellYIndex);
3095 appWidgetInfo.spanX = c.getInt(spanXIndex);
3096 appWidgetInfo.spanY = c.getInt(spanYIndex);
3097
3098 container = c.getInt(containerIndex);
3099 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
3100 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
3101 Log.e(TAG, "Widget found where container != " +
3102 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
3103 continue;
3104 }
3105
3106 appWidgetInfo.container = c.getInt(containerIndex);
3107 // check & update map of what's occupied
3108 deleteOnInvalidPlacement.set(false);
3109 if (!checkItemPlacement(occupied, appWidgetInfo,
3110 deleteOnInvalidPlacement)) {
3111 if (deleteOnInvalidPlacement.get()) {
3112 itemsToRemove.add(id);
3113 }
3114 break;
3115 }
3116
3117 String providerName = appWidgetInfo.providerName.flattenToString();
3118 if (!providerName.equals(savedProvider) ||
3119 (appWidgetInfo.restoreStatus != restoreStatus)) {
3120 ContentValues values = new ContentValues();
3121 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
3122 providerName);
3123 values.put(LauncherSettings.Favorites.RESTORED,
3124 appWidgetInfo.restoreStatus);
3125 String where = BaseColumns._ID + "= ?";
3126 String[] args = {Long.toString(id)};
3127 contentResolver.update(contentUri, values, where, args);
3128 }
3129 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
3130 sBgAppWidgets.add(appWidgetInfo);
3131 }
3132 break;
3133 }
3134 } catch (Exception e) {
3135 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
3136 }
3137 }
3138 } finally {
3139 if (c != null) {
3140 c.close();
3141 }
3142 }
3143
3144 // Break early if we've stopped loading
3145 if (mStopped) {
3146 clearSBgDataStructures();
3147 return false;
3148 }
3149
3150 if (itemsToRemove.size() > 0) {
3151 ContentProviderClient client = contentResolver.acquireContentProviderClient(
3152 contentUri);
3153 // Remove dead items
3154 for (long id : itemsToRemove) {
3155 if (DEBUG_LOADERS) {
3156 Log.d(TAG, "Removed id = " + id);
3157 }
3158 // Don't notify content observers
3159 try {
3160 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
3161 null, null);
3162 } catch (RemoteException e) {
3163 Log.w(TAG, "Could not remove id = " + id);
3164 }
3165 }
3166 }
3167
3168 if (restoredRows.size() > 0) {
3169 ContentProviderClient updater = contentResolver.acquireContentProviderClient(
3170 contentUri);
3171 // Update restored items that no longer require special handling
3172 try {
3173 StringBuilder selectionBuilder = new StringBuilder();
3174 selectionBuilder.append(LauncherSettings.Favorites._ID);
3175 selectionBuilder.append(" IN (");
3176 selectionBuilder.append(TextUtils.join(", ", restoredRows));
3177 selectionBuilder.append(")");
3178 ContentValues values = new ContentValues();
3179 values.put(LauncherSettings.Favorites.RESTORED, 0);
3180 updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
3181 values, selectionBuilder.toString(), null);
3182 } catch (RemoteException e) {
3183 Log.w(TAG, "Could not update restored rows");
3184 }
3185 }
3186
3187 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
3188 context.registerReceiver(new AppsAvailabilityCheck(),
3189 new IntentFilter(StartupReceiver.SYSTEM_READY),
3190 null, sWorker);
3191 }
3192
3193 if (loadedOldDb) {
3194 long maxScreenId = 0;
3195 // If we're importing we use the old screen order.
3196 for (ItemInfo item: sBgItemsIdMap.values()) {
3197 long screenId = item.screenId;
3198 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
3199 !sBgWorkspaceScreens.contains(screenId)) {
3200 sBgWorkspaceScreens.add(screenId);
3201 if (screenId > maxScreenId) {
3202 maxScreenId = screenId;
3203 }
3204 }
3205 }
3206 Collections.sort(sBgWorkspaceScreens);
3207 // Log to disk
3208 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
3209 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
3210 TextUtils.join(", ", sBgWorkspaceScreens), true);
3211
3212 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
3213 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
3214
3215 // Update the max item id after we load an old db
3216 long maxItemId = 0;
3217 // If we're importing we use the old screen order.
3218 for (ItemInfo item: sBgItemsIdMap.values()) {
3219 maxItemId = Math.max(maxItemId, item.id);
3220 }
3221 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
3222 } else {
3223 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
3224 for (Integer i : orderedScreens.keySet()) {
3225 sBgWorkspaceScreens.add(orderedScreens.get(i));
3226 }
3227 // Log to disk
3228 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
3229 TextUtils.join(", ", sBgWorkspaceScreens), true);
3230
3231 // Remove any empty screens
3232 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
3233 for (ItemInfo item: sBgItemsIdMap.values()) {
3234 long screenId = item.screenId;
3235 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
3236 unusedScreens.contains(screenId)) {
3237 unusedScreens.remove(screenId);
3238 }
3239 }
3240
3241 // If there are any empty screens remove them, and update.
3242 if (unusedScreens.size() != 0) {
3243 // Log to disk
3244 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
3245 TextUtils.join(", ", unusedScreens), true);
3246
3247 sBgWorkspaceScreens.removeAll(unusedScreens);
3248 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
3249 }
3250 }
3251
3252 if (DEBUG_LOADERS) {
3253 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
3254 Log.d(TAG, "workspace layout: ");
3255 int nScreens = occupied.size();
3256 for (int y = 0; y < countY; y++) {
3257 String line = "";
3258
3259 Iterator<Long> iter = occupied.keySet().iterator();
3260 while (iter.hasNext()) {
3261 long screenId = iter.next();
3262 if (screenId > 0) {
3263 line += " | ";
3264 }
3265 for (int x = 0; x < countX; x++) {
3266 ItemInfo[][] screen = occupied.get(screenId);
3267 if (x < screen.length && y < screen[x].length) {
3268 line += (screen[x][y] != null) ? "#" : ".";
3269 } else {
3270 line += "!";
3271 }
3272 }
3273 }
3274 Log.d(TAG, "[ " + line + " ]");
3275 }
3276 }
3277 }
3278 return loadedOldDb;
3279 }
3280
3281 /** Filters the set of items who are directly or indirectly (via another container) on the
3282 * specified screen. */
3283 private void filterCurrentWorkspaceItems(long currentScreenId,
3284 ArrayList<ItemInfo> allWorkspaceItems,
3285 ArrayList<ItemInfo> currentScreenItems,
3286 ArrayList<ItemInfo> otherScreenItems) {
3287 // Purge any null ItemInfos
3288 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
3289 while (iter.hasNext()) {
3290 ItemInfo i = iter.next();
3291 if (i == null) {
3292 iter.remove();
3293 }
3294 }
3295
3296 // Order the set of items by their containers first, this allows use to walk through the
3297 // list sequentially, build up a list of containers that are in the specified screen,
3298 // as well as all items in those containers.
3299 Set<Long> itemsOnScreen = new HashSet<Long>();
3300 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
3301 @Override
3302 public int compare(ItemInfo lhs, ItemInfo rhs) {
3303 return (int) (lhs.container - rhs.container);
3304 }
3305 });
3306 for (ItemInfo info : allWorkspaceItems) {
3307 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
3308 if (info.screenId == currentScreenId) {
3309 currentScreenItems.add(info);
3310 itemsOnScreen.add(info.id);
3311 } else {
3312 otherScreenItems.add(info);
3313 }
3314 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
3315 currentScreenItems.add(info);
3316 itemsOnScreen.add(info.id);
3317 } else {
3318 if (itemsOnScreen.contains(info.container)) {
3319 currentScreenItems.add(info);
3320 itemsOnScreen.add(info.id);
3321 } else {
3322 otherScreenItems.add(info);
3323 }
3324 }
3325 }
3326 }
3327
3328 /** Filters the set of widgets which are on the specified screen. */
3329 private void filterCurrentAppWidgets(long currentScreenId,
3330 ArrayList<LauncherAppWidgetInfo> appWidgets,
3331 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
3332 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
3333
3334 for (LauncherAppWidgetInfo widget : appWidgets) {
3335 if (widget == null) continue;
3336 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
3337 widget.screenId == currentScreenId) {
3338 currentScreenWidgets.add(widget);
3339 } else {
3340 otherScreenWidgets.add(widget);
3341 }
3342 }
3343 }
3344
3345 /** Filters the set of folders which are on the specified screen. */
3346 private void filterCurrentFolders(long currentScreenId,
3347 HashMap<Long, ItemInfo> itemsIdMap,
3348 HashMap<Long, FolderInfo> folders,
3349 HashMap<Long, FolderInfo> currentScreenFolders,
3350 HashMap<Long, FolderInfo> otherScreenFolders) {
3351
3352 for (long id : folders.keySet()) {
3353 ItemInfo info = itemsIdMap.get(id);
3354 FolderInfo folder = folders.get(id);
3355 if (info == null || folder == null) continue;
3356 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
3357 info.screenId == currentScreenId) {
3358 currentScreenFolders.put(id, folder);
3359 } else {
3360 otherScreenFolders.put(id, folder);
3361 }
3362 }
3363 }
3364
3365 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
3366 * right) */
3367 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
3368 final LauncherAppState app = LauncherAppState.getInstance();
3369 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3370 // XXX: review this
3371 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
3372 @Override
3373 public int compare(ItemInfo lhs, ItemInfo rhs) {
3374 int cellCountX = (int) grid.numColumns;
3375 int cellCountY = (int) grid.numRows;
3376 int screenOffset = cellCountX * cellCountY;
3377 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
3378 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
3379 lhs.cellY * cellCountX + lhs.cellX);
3380 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
3381 rhs.cellY * cellCountX + rhs.cellX);
3382 return (int) (lr - rr);
3383 }
3384 });
3385 }
3386
3387 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
3388 final ArrayList<Long> orderedScreens) {
3389 final Runnable r = new Runnable() {
3390 @Override
3391 public void run() {
3392 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3393 if (callbacks != null) {
3394 callbacks.bindScreens(orderedScreens);
3395 }
3396 }
3397 };
3398 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3399 }
3400
3401 private void bindWorkspaceItems(final Callbacks oldCallbacks,
3402 final ArrayList<ItemInfo> workspaceItems,
3403 final ArrayList<LauncherAppWidgetInfo> appWidgets,
3404 final HashMap<Long, FolderInfo> folders,
3405 ArrayList<Runnable> deferredBindRunnables) {
3406
3407 final boolean postOnMainThread = (deferredBindRunnables != null);
3408
3409 // Bind the workspace items
3410 int N = workspaceItems.size();
3411 for (int i = 0; i < N; i += ITEMS_CHUNK) {
3412 final int start = i;
3413 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
3414 final Runnable r = new Runnable() {
3415 @Override
3416 public void run() {
3417 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3418 if (callbacks != null) {
3419 callbacks.bindItems(workspaceItems, start, start+chunkSize,
3420 false);
3421 }
3422 }
3423 };
3424 if (postOnMainThread) {
3425 synchronized (deferredBindRunnables) {
3426 deferredBindRunnables.add(r);
3427 }
3428 } else {
3429 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3430 }
3431 }
3432
3433 // Bind the folders
3434 if (!folders.isEmpty()) {
3435 final Runnable r = new Runnable() {
3436 public void run() {
3437 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3438 if (callbacks != null) {
3439 callbacks.bindFolders(folders);
3440 }
3441 }
3442 };
3443 if (postOnMainThread) {
3444 synchronized (deferredBindRunnables) {
3445 deferredBindRunnables.add(r);
3446 }
3447 } else {
3448 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3449 }
3450 }
3451
3452 // Bind the widgets, one at a time
3453 N = appWidgets.size();
3454 for (int i = 0; i < N; i++) {
3455 final LauncherAppWidgetInfo widget = appWidgets.get(i);
3456 final Runnable r = new Runnable() {
3457 public void run() {
3458 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3459 if (callbacks != null) {
3460 callbacks.bindAppWidget(widget);
3461 }
3462 }
3463 };
3464 if (postOnMainThread) {
3465 deferredBindRunnables.add(r);
3466 } else {
3467 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3468 }
3469 }
3470 }
3471
3472 /**
3473 * Binds all loaded data to actual views on the main thread.
3474 */
3475 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
3476 final long t = SystemClock.uptimeMillis();
3477 Runnable r;
3478
3479 // Don't use these two variables in any of the callback runnables.
3480 // Otherwise we hold a reference to them.
3481 final Callbacks oldCallbacks = mCallbacks.get();
3482 if (oldCallbacks == null) {
3483 // This launcher has exited and nobody bothered to tell us. Just bail.
3484 Log.w(TAG, "LoaderTask running with no launcher");
3485 return;
3486 }
3487
3488 // Save a copy of all the bg-thread collections
3489 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
3490 ArrayList<LauncherAppWidgetInfo> appWidgets =
3491 new ArrayList<LauncherAppWidgetInfo>();
3492 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
3493 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
3494 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
3495 synchronized (sBgLock) {
3496 workspaceItems.addAll(sBgWorkspaceItems);
3497 appWidgets.addAll(sBgAppWidgets);
3498 folders.putAll(sBgFolders);
3499 itemsIdMap.putAll(sBgItemsIdMap);
3500 orderedScreenIds.addAll(sBgWorkspaceScreens);
3501 }
3502
3503 final boolean isLoadingSynchronously =
3504 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
3505 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
3506 oldCallbacks.getCurrentWorkspaceScreen();
3507 if (currScreen >= orderedScreenIds.size()) {
3508 // There may be no workspace screens (just hotseat items and an empty page).
3509 currScreen = PagedView.INVALID_RESTORE_PAGE;
3510 }
3511 final int currentScreen = currScreen;
3512 final long currentScreenId = currentScreen < 0
3513 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
3514
3515 // Load all the items that are on the current page first (and in the process, unbind
3516 // all the existing workspace items before we call startBinding() below.
3517 unbindWorkspaceItemsOnMainThread();
3518
3519 // Separate the items that are on the current screen, and all the other remaining items
3520 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
3521 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
3522 ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
3523 new ArrayList<LauncherAppWidgetInfo>();
3524 ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
3525 new ArrayList<LauncherAppWidgetInfo>();
3526 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
3527 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
3528
3529 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
3530 otherWorkspaceItems);
3531 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
3532 otherAppWidgets);
3533 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
3534 otherFolders);
3535 sortWorkspaceItemsSpatially(currentWorkspaceItems);
3536 sortWorkspaceItemsSpatially(otherWorkspaceItems);
3537
3538 // Tell the workspace that we're about to start binding items
3539 r = new Runnable() {
3540 public void run() {
3541 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3542 if (callbacks != null) {
3543 callbacks.startBinding();
3544 }
3545 }
3546 };
3547 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3548
3549 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
3550
3551 // Load items on the current page
3552 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
3553 currentFolders, null);
3554 if (isLoadingSynchronously) {
3555 r = new Runnable() {
3556 public void run() {
3557 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3558 if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
3559 callbacks.onPageBoundSynchronously(currentScreen);
3560 }
3561 }
3562 };
3563 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3564 }
3565
3566 // Load all the remaining pages (if we are loading synchronously, we want to defer this
3567 // work until after the first render)
3568 synchronized (mDeferredBindRunnables) {
3569 mDeferredBindRunnables.clear();
3570 }
3571 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
3572 (isLoadingSynchronously ? mDeferredBindRunnables : null));
3573
3574 // Tell the workspace that we're done binding items
3575 r = new Runnable() {
3576 public void run() {
3577 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3578 if (callbacks != null) {
3579 callbacks.finishBindingItems(isUpgradePath);
3580 }
3581
3582 // If we're profiling, ensure this is the last thing in the queue.
3583 if (DEBUG_LOADERS) {
3584 Log.d(TAG, "bound workspace in "
3585 + (SystemClock.uptimeMillis()-t) + "ms");
3586 }
3587
3588 mIsLoadingAndBindingWorkspace = false;
3589 }
3590 };
3591 if (isLoadingSynchronously) {
3592 synchronized (mDeferredBindRunnables) {
3593 mDeferredBindRunnables.add(r);
3594 }
3595 } else {
3596 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
3597 }
3598 }
3599
3600 private void loadAndBindAllApps() {
3601 if (DEBUG_LOADERS) {
3602 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
3603 }
3604 if (!mAllAppsLoaded) {
3605 loadAllApps();
3606 synchronized (LoaderTask.this) {
3607 if (mStopped) {
3608 return;
3609 }
3610 mAllAppsLoaded = true;
3611 }
3612 } else {
3613 onlyBindAllApps();
3614 }
3615 }
3616
3617 private void onlyBindAllApps() {
3618 final Callbacks oldCallbacks = mCallbacks.get();
3619 if (oldCallbacks == null) {
3620 // This launcher has exited and nobody bothered to tell us. Just bail.
3621 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
3622 return;
3623 }
3624
3625 // shallow copy
3626 @SuppressWarnings("unchecked")
3627 final ArrayList<AppInfo> list
3628 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
3629 Runnable r = new Runnable() {
3630 public void run() {
3631 final long t = SystemClock.uptimeMillis();
3632 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3633 if (callbacks != null) {
3634 callbacks.bindAllApplications(list);
3635 }
3636 if (DEBUG_LOADERS) {
3637 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
3638 + (SystemClock.uptimeMillis()-t) + "ms");
3639 }
3640 }
3641 };
3642 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
3643 if (isRunningOnMainThread) {
3644 r.run();
3645 } else {
3646 mHandler.post(r);
3647 }
3648 }
3649
3650 private void loadAllApps() {
3651 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
3652
3653 final Callbacks oldCallbacks = mCallbacks.get();
3654 if (oldCallbacks == null) {
3655 // This launcher has exited and nobody bothered to tell us. Just bail.
3656 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
3657 return;
3658 }
3659
3660 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
3661 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
3662
3663 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
3664
3665 // Clear the list of apps
3666 mBgAllAppsList.clear();
3667 for (UserHandleCompat user : profiles) {
3668 // Query for the set of apps
3669 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
3670 List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
3671 if (DEBUG_LOADERS) {
3672 Log.d(TAG, "getActivityList took "
3673 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
3674 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
3675 }
3676 // Fail if we don't have any apps
3677 if (apps == null || apps.isEmpty()) {
3678 return;
3679 }
3680 // Sort the applications by name
3681 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
3682 Collections.sort(apps,
3683 new LauncherModel.ShortcutNameComparator(mLabelCache));
3684 if (DEBUG_LOADERS) {
3685 Log.d(TAG, "sort took "
3686 + (SystemClock.uptimeMillis()-sortTime) + "ms");
3687 }
3688
3689 // Create the ApplicationInfos
3690 for (int i = 0; i < apps.size(); i++) {
3691 LauncherActivityInfoCompat app = apps.get(i);
3692 // This builds the icon bitmaps.
3693 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
3694 }
3695 }
3696 // Huh? Shouldn't this be inside the Runnable below?
3697 final ArrayList<AppInfo> added = mBgAllAppsList.added;
3698 mBgAllAppsList.added = new ArrayList<AppInfo>();
3699
3700 // Post callback on main thread
3701 mHandler.post(new Runnable() {
3702 public void run() {
3703 final long bindTime = SystemClock.uptimeMillis();
3704 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
3705 if (callbacks != null) {
3706 callbacks.bindAllApplications(added);
3707 if (DEBUG_LOADERS) {
3708 Log.d(TAG, "bound " + added.size() + " apps in "
3709 + (SystemClock.uptimeMillis() - bindTime) + "ms");
3710 }
3711 } else {
3712 Log.i(TAG, "not binding apps: no Launcher activity");
3713 }
3714 }
3715 });
3716
3717 if (DEBUG_LOADERS) {
3718 Log.d(TAG, "Icons processed in "
3719 + (SystemClock.uptimeMillis() - loadTime) + "ms");
3720 }
3721 }
3722
3723 public void dumpState() {
3724 synchronized (sBgLock) {
3725 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
3726 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
3727 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
3728 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
3729 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
3730 }
3731 }
3732 }
3733
3734 void enqueuePackageUpdated(PackageUpdatedTask task) {
3735 sWorker.post(task);
3736 }
3737
3738 private class AppsAvailabilityCheck extends BroadcastReceiver {
3739
3740 @Override
3741 public void onReceive(Context context, Intent intent) {
3742 synchronized (sBgLock) {
3743 final LauncherAppsCompat launcherApps = LauncherAppsCompat
3744 .getInstance(mApp.getContext());
3745 ArrayList<String> packagesRemoved;
3746 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
3747 UserHandleCompat user = entry.getKey();
3748 packagesRemoved = new ArrayList<String>();
3749 for (String pkg : entry.getValue()) {
3750 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
3751 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
3752 packagesRemoved.add(pkg);
3753 }
3754 }
3755 if (!packagesRemoved.isEmpty()) {
3756 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
3757 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
3758 }
3759 }
3760 sPendingPackages.clear();
3761 }
3762 }
3763 }
3764
3765 /**
3766 * Workaround to re-check unrestored items, in-case they were installed but the Package-ADD
3767 * runnable was missed by the launcher.
3768 */
3769 public void recheckRestoredItems(final Context context) {
3770 Runnable r = new Runnable() {
3771
3772 @Override
3773 public void run() {
3774 LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3775 HashSet<String> installedPackages = new HashSet<String>();
3776 UserHandleCompat user = UserHandleCompat.myUserHandle();
3777 synchronized(sBgLock) {
3778 for (ItemInfo info : sBgItemsIdMap.values()) {
3779 if (info instanceof ShortcutInfo) {
3780 ShortcutInfo si = (ShortcutInfo) info;
3781 if (si.isPromise() && si.getTargetComponent() != null
3782 && launcherApps.isPackageEnabledForProfile(
3783 si.getTargetComponent().getPackageName(), user)) {
3784 installedPackages.add(si.getTargetComponent().getPackageName());
3785 }
3786 } else if (info instanceof LauncherAppWidgetInfo) {
3787 LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) info;
3788 if (widget.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
3789 && launcherApps.isPackageEnabledForProfile(
3790 widget.providerName.getPackageName(), user)) {
3791 installedPackages.add(widget.providerName.getPackageName());
3792 }
3793 }
3794 }
3795 }
3796
3797 if (!installedPackages.isEmpty()) {
3798 final ArrayList<AppInfo> restoredApps = new ArrayList<AppInfo>();
3799 for (String pkg : installedPackages) {
3800 for (LauncherActivityInfoCompat info : launcherApps.getActivityList(pkg, user)) {
3801 restoredApps.add(new AppInfo(context, info, user, mIconCache, null));
3802 }
3803 }
3804
3805 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
3806 if (!restoredApps.isEmpty()) {
3807 mHandler.post(new Runnable() {
3808 public void run() {
3809 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3810 if (callbacks == cb && cb != null) {
3811 callbacks.bindAppsRestored(restoredApps);
3812 }
3813 }
3814 });
3815 }
3816
3817 }
3818 }
3819 };
3820 sWorker.post(r);
3821 }
3822
3823 private class PackageUpdatedTask implements Runnable {
3824 int mOp;
3825 String[] mPackages;
3826 UserHandleCompat mUser;
3827
3828 public static final int OP_NONE = 0;
3829 public static final int OP_ADD = 1;
3830 public static final int OP_UPDATE = 2;
3831 public static final int OP_REMOVE = 3; // uninstlled
3832 public static final int OP_UNAVAILABLE = 4; // external media unmounted
3833
3834
3835 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
3836 mOp = op;
3837 mPackages = packages;
3838 mUser = user;
3839 }
3840
3841 public void run() {
3842 final Context context = mApp.getContext();
3843
3844 final String[] packages = mPackages;
3845 final int N = packages.length;
3846 switch (mOp) {
3847 case OP_ADD:
3848 for (int i=0; i<N; i++) {
3849 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
3850 mIconCache.remove(packages[i], mUser);
3851 mBgAllAppsList.addPackage(context, packages[i], mUser);
3852 }
3853 break;
3854 case OP_UPDATE:
3855 for (int i=0; i<N; i++) {
3856 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
3857 mBgAllAppsList.updatePackage(context, packages[i], mUser);
3858 WidgetPreviewLoader.removePackageFromDb(
3859 mApp.getWidgetPreviewCacheDb(), packages[i]);
3860 }
3861 break;
3862 case OP_REMOVE:
3863 case OP_UNAVAILABLE:
3864 for (int i=0; i<N; i++) {
3865 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
3866 mBgAllAppsList.removePackage(packages[i], mUser);
3867 WidgetPreviewLoader.removePackageFromDb(
3868 mApp.getWidgetPreviewCacheDb(), packages[i]);
3869 }
3870 break;
3871 }
3872
3873 ArrayList<AppInfo> added = null;
3874 ArrayList<AppInfo> modified = null;
3875 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
3876
3877 if (mBgAllAppsList.added.size() > 0) {
3878 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
3879 mBgAllAppsList.added.clear();
3880 }
3881 if (mBgAllAppsList.modified.size() > 0) {
3882 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
3883 mBgAllAppsList.modified.clear();
3884 }
3885 if (mBgAllAppsList.removed.size() > 0) {
3886 removedApps.addAll(mBgAllAppsList.removed);
3887 mBgAllAppsList.removed.clear();
3888 }
3889
3890 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
3891 if (callbacks == null) {
3892 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
3893 return;
3894 }
3895
3896 if (added != null) {
3897 // Ensure that we add all the workspace applications to the db
3898 if (LauncherAppState.isDisableAllApps()) {
3899 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
3900 addAndBindAddedWorkspaceApps(context, addedInfos);
3901 } else {
3902 addAppsToAllApps(context, added);
3903 }
3904 }
3905
3906 if (modified != null) {
3907 final ArrayList<AppInfo> modifiedFinal = modified;
3908
3909 // Update the launcher db to reflect the changes
3910 for (AppInfo a : modifiedFinal) {
3911 ArrayList<ItemInfo> infos =
3912 getItemInfoForComponentName(a.componentName, mUser);
3913 for (ItemInfo i : infos) {
3914 if (isShortcutInfoUpdateable(i)) {
3915 ShortcutInfo info = (ShortcutInfo) i;
3916 info.title = a.title.toString();
3917 info.contentDescription = a.contentDescription;
3918 updateItemInDatabase(context, info);
3919 }
3920 }
3921 }
3922
3923 mHandler.post(new Runnable() {
3924 public void run() {
3925 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3926 if (callbacks == cb && cb != null) {
3927 callbacks.bindAppsUpdated(modifiedFinal);
3928 }
3929 }
3930 });
3931 }
3932
3933 final ArrayList<String> removedPackageNames =
3934 new ArrayList<String>();
3935 if (mOp == OP_REMOVE) {
3936 // Mark all packages in the broadcast to be removed
3937 removedPackageNames.addAll(Arrays.asList(packages));
3938 } else if (mOp == OP_UPDATE) {
3939 // Mark disabled packages in the broadcast to be removed
3940 final PackageManager pm = context.getPackageManager();
3941 for (int i=0; i<N; i++) {
3942 if (isPackageDisabled(context, packages[i], mUser)) {
3943 removedPackageNames.add(packages[i]);
3944 }
3945 }
3946 }
3947 // Remove all the components associated with this package
3948 for (String pn : removedPackageNames) {
3949 deletePackageFromDatabase(context, pn, mUser);
3950 }
3951 // Remove all the specific components
3952 for (AppInfo a : removedApps) {
3953 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
3954 deleteItemsFromDatabase(context, infos);
3955 }
3956 if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
3957 // Remove any queued items from the install queue
3958 String spKey = LauncherAppState.getSharedPreferencesKey();
3959 SharedPreferences sp =
3960 context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
3961 InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
3962 // Call the components-removed callback
3963 mHandler.post(new Runnable() {
3964 public void run() {
3965 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3966 if (callbacks == cb && cb != null) {
3967 callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
3968 }
3969 }
3970 });
3971 }
3972
3973 final ArrayList<Object> widgetsAndShortcuts =
3974 getSortedWidgetsAndShortcuts(context);
3975 mHandler.post(new Runnable() {
3976 @Override
3977 public void run() {
3978 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3979 if (callbacks == cb && cb != null) {
3980 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
3981 }
3982 }
3983 });
3984
3985 // Write all the logs to disk
3986 mHandler.post(new Runnable() {
3987 public void run() {
3988 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3989 if (callbacks == cb && cb != null) {
3990 callbacks.dumpLogsToLocalData();
3991 }
3992 }
3993 });
3994 }
3995 }
3996
3997 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
3998 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
3999 PackageManager packageManager = context.getPackageManager();
4000 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
4001 widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
4002
4003 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
4004 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
4005 Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
4006 return widgetsAndShortcuts;
4007 }
4008
4009 private static boolean isPackageDisabled(Context context, String packageName,
4010 UserHandleCompat user) {
4011 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
4012 return !launcherApps.isPackageEnabledForProfile(packageName, user);
4013 }
4014
4015 public static boolean isValidPackageActivity(Context context, ComponentName cn,
4016 UserHandleCompat user) {
4017 if (cn == null) {
4018 return false;
4019 }
4020 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
4021 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
4022 return false;
4023 }
4024 return launcherApps.isActivityEnabledForProfile(cn, user);
4025 }
4026
4027 public static boolean isValidPackage(Context context, String packageName,
4028 UserHandleCompat user) {
4029 if (packageName == null) {
4030 return false;
4031 }
4032 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
4033 return launcherApps.isPackageEnabledForProfile(packageName, user);
4034 }
4035
4036 /**
4037 * Make an ShortcutInfo object for a restored application or shortcut item that points
4038 * to a package that is not yet installed on the system.
4039 */
4040 <<<<<<< GitAnalyzerPlus_ours
4041 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
4042 int promiseType) {
4043 ||||||| GitAnalyzerPlus_base
4044 public void run() {
4045 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
4046 if (callbacks == cb && cb != null) {
4047 callbacks.bindComponentsRemoved(removedPackageNames, removedApps);
4048 }
4049 }
4050 });
4051 }
4052
4053 final ArrayList<Object> widgetsAndShortcuts =
4054 getSortedWidgetsAndShortcuts(context);
4055 mHandler.post(new Runnable() {
4056 @Override
4057 public void run() {
4058 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
4059 if (callbacks == cb && cb != null) {
4060 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
4061 }
4062 }
4063 });
4064
4065 // Write all the logs to disk
4066 mHandler.post(new Runnable() {
4067 public void run() {
4068 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
4069 if (callbacks == cb && cb != null) {
4070 callbacks.dumpLogsToLocalData();
4071 }
4072 }
4073 });
4074 }
4075 }
4076
4077 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
4078 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
4079 PackageManager packageManager = context.getPackageManager();
4080 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
4081 widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
4082 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
4083 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
4084 Collections.sort(widgetsAndShortcuts,
4085 new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
4086 return widgetsAndShortcuts;
4087 }
4088
4089 private static boolean isPackageDisabled(PackageManager pm, String packageName) {
4090 try {
4091 PackageInfo pi = pm.getPackageInfo(packageName, 0);
4092 return !pi.applicationInfo.enabled;
4093 } catch (NameNotFoundException e) {
4094 // Fall through
4095 }
4096 return false;
4097 }
4098
4099 public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
4100 if (cn == null) {
4101 return false;
4102 }
4103 if (isPackageDisabled(pm, cn.getPackageName())) {
4104 return false;
4105 }
4106
4107 try {
4108 // Check the activity
4109 PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
4110 return (pm.getActivityInfo(cn, 0) != null);
4111 } catch (NameNotFoundException e) {
4112 return false;
4113 }
4114 }
4115
4116 /**
4117 * Make an ShortcutInfo object for a restored application or shortcut item that points
4118 * to a package that is not yet installed on the system.
4119 */
4120 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) {
4121 final ShortcutInfo info = new ShortcutInfo();
4122 info.usingFallbackIcon = true;
4123 info.setIcon(getFallbackIcon());
4124 if (cursor != null) {
4125 info.title = cursor.getString(titleIndex);
4126 } else {
4127 info.title = "";
4128 }
4129 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
4130 info.restoredIntent = intent;
4131 return info;
4132 }
4133
4134 /**
4135 * Make an Intent object for a restored application or shortcut item that points
4136 * to the market page for the item.
4137 */
4138 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
4139 final boolean debug = false;
4140 ComponentName componentName = intent.getComponent();
4141 Intent marketIntent = new Intent(Intent.ACTION_VIEW);
4142 Uri marketUri = new Uri.Builder()
4143 .scheme("market")
4144 .authority("details")
4145 .appendQueryParameter("id", componentName.getPackageName())
4146 .build();
4147 if (debug) Log.d(TAG, "manufactured intent uri: " + marketUri.toString());
4148 marketIntent.setData(marketUri);
4149 return marketIntent;
4150 }
4151
4152 /**
4153 * This is called from the code that adds shortcuts from the intent receiver. This
4154 * doesn't have a Cursor, but
4155 */
4156 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
4157 return getShortcutInfo(manager, intent, context, null, -1, -1, null);
4158 }
4159
4160 /**
4161 * Make an ShortcutInfo object for a shortcut that is an application.
4162 *
4163 * If c is not null, then it will be used to fill in missing data like the title and icon.
4164 */
4165 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
4166 Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
4167 ComponentName componentName = intent.getComponent();
4168 final ShortcutInfo info = new ShortcutInfo();
4169 if (componentName != null && !isValidPackageComponent(manager, componentName)) {
4170 Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
4171 return null;
4172 } else {
4173 try {
4174 PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
4175 info.initFlagsAndFirstInstallTime(pi);
4176 } catch (NameNotFoundException e) {
4177 Log.d(TAG, "getPackInfo failed for package " +
4178 componentName.getPackageName());
4179 }
4180 }
4181
4182 // TODO: See if the PackageManager knows about this case. If it doesn't
4183 // then return null & delete this.
4184
4185 // the resource -- This may implicitly give us back the fallback icon,
4186 // but don't worry about that. All we're doing with usingFallbackIcon is
4187 // to avoid saving lots of copies of that in the database, and most apps
4188 // have icons anyway.
4189
4190 // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
4191 // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
4192 // via resolveActivity().
4193 Bitmap icon = null;
4194 ResolveInfo resolveInfo = null;
4195 ComponentName oldComponent = intent.getComponent();
4196 Intent newIntent = new Intent(intent.getAction(), null);
4197 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
4198 newIntent.setPackage(oldComponent.getPackageName());
4199 List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
4200 for (ResolveInfo i : infos) {
4201 ComponentName cn = new ComponentName(i.activityInfo.packageName,
4202 i.activityInfo.name);
4203 if (cn.equals(oldComponent)) {
4204 resolveInfo = i;
4205 }
4206 }
4207 if (resolveInfo == null) {
4208 resolveInfo = manager.resolveActivity(intent, 0);
4209 }
4210 if (resolveInfo != null) {
4211 icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
4212 }
4213 // the db
4214 if (icon == null) {
4215 if (c != null) {
4216 icon = getIconFromCursor(c, iconIndex, context);
4217 }
4218 }
4219 // the fallback icon
4220 if (icon == null) {
4221 icon = getFallbackIcon();
4222 info.usingFallbackIcon = true;
4223 }
4224 info.setIcon(icon);
4225
4226 // from the resource
4227 if (resolveInfo != null) {
4228 ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
4229 if (labelCache != null && labelCache.containsKey(key)) {
4230 info.title = labelCache.get(key);
4231 } else {
4232 info.title = resolveInfo.activityInfo.loadLabel(manager);
4233 if (labelCache != null) {
4234 labelCache.put(key, info.title);
4235 }
4236 }
4237 }
4238 // from the db
4239 if (info.title == null) {
4240 if (c != null) {
4241 info.title = c.getString(titleIndex);
4242 }
4243 }
4244 // fall back to the class name of the activity
4245 if (info.title == null) {
4246 info.title = componentName.getClassName();
4247 }
4248 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
4249 return info;
4250 }
4251
4252 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
4253 ItemInfoFilter f) {
4254 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
4255 for (ItemInfo i : infos) {
4256 if (i instanceof ShortcutInfo) {
4257 ShortcutInfo info = (ShortcutInfo) i;
4258 ComponentName cn = info.intent.getComponent();
4259 if (cn != null && f.filterItem(null, info, cn)) {
4260 filtered.add(info);
4261 }
4262 } else if (i instanceof FolderInfo) {
4263 FolderInfo info = (FolderInfo) i;
4264 for (ShortcutInfo s : info.contents) {
4265 ComponentName cn = s.intent.getComponent();
4266 if (cn != null && f.filterItem(info, s, cn)) {
4267 filtered.add(s);
4268 }
4269 }
4270 } else if (i instanceof LauncherAppWidgetInfo) {
4271 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
4272 ComponentName cn = info.providerName;
4273 if (cn != null && f.filterItem(null, info, cn)) {
4274 filtered.add(info);
4275 }
4276 }
4277 }
4278 return new ArrayList<ItemInfo>(filtered);
4279 }
4280
4281 private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
4282 ItemInfoFilter filter = new ItemInfoFilter() {
4283 @Override
4284 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
4285 return cn.getPackageName().equals(pn);
4286 }
4287 };
4288 return filterItemInfos(sBgItemsIdMap.values(), filter);
4289 }
4290
4291 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
4292 ItemInfoFilter filter = new ItemInfoFilter() {
4293 @Override
4294 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
4295 return cn.equals(cname);
4296 }
4297 };
4298 return filterItemInfos(sBgItemsIdMap.values(), filter);
4299 }
4300
4301 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
4302 if (i instanceof ShortcutInfo) {
4303 ShortcutInfo info = (ShortcutInfo) i;
4304 // We need to check for ACTION_MAIN otherwise getComponent() might
4305 // return null for some shortcuts (for instance, for shortcuts to
4306 // web pages.)
4307 Intent intent = info.intent;
4308 ComponentName name = intent.getComponent();
4309 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
4310 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
4311 return true;
4312 }
4313 // placeholder shortcuts get special treatment, let them through too.
4314 if (info.getRestoredIntent() != null) {
4315 return true;
4316 }
4317 }
4318 return false;
4319 }
4320
4321 /**
4322 * Make an ShortcutInfo object for a shortcut that isn't an application.
4323 */
4324 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
4325 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
4326 int titleIndex) {
4327
4328 Bitmap icon = null;
4329 final ShortcutInfo info = new ShortcutInfo();
4330 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
4331
4332 // TODO: If there's an explicit component and we can't install that, delete it.
4333
4334 info.title = c.getString(titleIndex);
4335
4336 int iconType = c.getInt(iconTypeIndex);
4337 switch (iconType) {
4338 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
4339 String packageName = c.getString(iconPackageIndex);
4340 String resourceName = c.getString(iconResourceIndex);
4341 PackageManager packageManager = context.getPackageManager();
4342 info.customIcon = false;
4343 // the resource
4344 try {
4345 Resources resources = packageManager.getResourcesForApplication(packageName);
4346 if (resources != null) {
4347 final int id = resources.getIdentifier(resourceName, null, null);
4348 icon = Utilities.createIconBitmap(
4349 mIconCache.getFullResIcon(resources, id), context);
4350 }
4351 } catch (Exception e) {
4352 // drop this. we have other places to look for icons
4353 }
4354 // the db
4355 if (icon == null) {
4356 icon = getIconFromCursor(c, iconIndex, context);
4357 }
4358 // the fallback icon
4359 if (icon == null) {
4360 icon = getFallbackIcon();
4361 info.usingFallbackIcon = true;
4362 }
4363 break;
4364 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
4365 icon = getIconFromCursor(c, iconIndex, context);
4366 if (icon == null) {
4367 icon = getFallbackIcon();
4368 info.customIcon = false;
4369 info.usingFallbackIcon = true;
4370 } else {
4371 info.customIcon = true;
4372 }
4373 break;
4374 default:
4375 icon = getFallbackIcon();
4376 info.usingFallbackIcon = true;
4377 info.customIcon = false;
4378 break;
4379 }
4380 info.setIcon(icon);
4381 return info;
4382 }
4383
4384 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
4385 @SuppressWarnings("all") // suppress dead code warning
4386 final boolean debug = false;
4387 if (debug) {
4388 Log.d(TAG, "getIconFromCursor app="
4389 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
4390 }
4391 byte[] data = c.getBlob(iconIndex);
4392 try {
4393 return Utilities.createIconBitmap(
4394 BitmapFactory.decodeByteArray(data, 0, data.length), context);
4395 } catch (Exception e) {
4396 return null;
4397 }
4398 }
4399
4400 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
4401 int cellX, int cellY, boolean notify) {
4402 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
4403 if (info == null) {
4404 return null;
4405 }
4406 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
4407
4408 return info;
4409 }
4410
4411 /**
4412 * Attempts to find an AppWidgetProviderInfo that matches the given component.
4413 */
4414 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
4415 ComponentName component) {
4416 List<AppWidgetProviderInfo> widgets =
4417 AppWidgetManager.getInstance(context).getInstalledProviders();
4418 for (AppWidgetProviderInfo info : widgets) {
4419 if (info.provider.equals(component)) {
4420 return info;
4421 }
4422 }
4423 return null;
4424 }
4425
4426 /**
4427 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
4428 */
4429 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
4430 final PackageManager packageManager = context.getPackageManager();
4431 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
4432 new ArrayList<WidgetMimeTypeHandlerData>();
4433
4434 final Intent supportsIntent =
4435 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
4436 supportsIntent.setType(mimeType);
4437
4438 // Create a set of widget configuration components that we can test against
4439 final List<AppWidgetProviderInfo> widgets =
4440 AppWidgetManager.getInstance(context).getInstalledProviders();
4441 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
4442 new HashMap<ComponentName, AppWidgetProviderInfo>();
4443 for (AppWidgetProviderInfo info : widgets) {
4444 configurationComponentToWidget.put(info.configure, info);
4445 }
4446
4447 // Run through each of the intents that can handle this type of clip data, and cross
4448 // reference them with the components that are actual configuration components
4449 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
4450 PackageManager.MATCH_DEFAULT_ONLY);
4451 for (ResolveInfo info : activities) {
4452 final ActivityInfo activityInfo = info.activityInfo;
4453 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
4454 activityInfo.name);
4455 if (configurationComponentToWidget.containsKey(infoComponent)) {
4456 supportedConfigurationActivities.add(
4457 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
4458 configurationComponentToWidget.get(infoComponent)));
4459 }
4460 }
4461 return supportedConfigurationActivities;
4462 }
4463
4464 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
4465 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
4466 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
4467 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
4468
4469 if (intent == null) {
4470 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
4471 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
4472 return null;
4473 }
4474
4475 Bitmap icon = null;
4476 boolean customIcon = false;
4477 ShortcutIconResource iconResource = null;
4478
4479 if (bitmap != null && bitmap instanceof Bitmap) {
4480 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
4481 customIcon = true;
4482 } else {
4483 =======
4484 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex) {
4485 >>>>>>> GitAnalyzerPlus_theirs
4486 final ShortcutInfo info = new ShortcutInfo();
4487 info.user = UserHandleCompat.myUserHandle();
4488 mIconCache.getTitleAndIcon(info, intent, info.user, true);
4489
4490 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
4491 String title = (cursor != null) ? cursor.getString(titleIndex) : null;
4492 if (!TextUtils.isEmpty(title)) {
4493 info.title = title;
4494 }
4495 info.status = ShortcutInfo.FLAG_RESTORED_ICON;
4496 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
4497 if (TextUtils.isEmpty(info.title)) {
4498 info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
4499 }
4500 info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
4501 } else {
4502 throw new InvalidParameterException("Invalid restoreType " + promiseType);
4503 }
4504
4505 info.contentDescription = mUserManager.getBadgedLabelForUser(
4506 info.title.toString(), info.user);
4507 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
4508 <<<<<<< GitAnalyzerPlus_ours
4509 info.promisedIntent = intent;
4510 ||||||| GitAnalyzerPlus_base
4511 getSortedWidgetsAndShortcuts(context);
4512 mHandler.post(new Runnable() {
4513 @Override
4514 public void run() {
4515 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
4516 if (callbacks == cb && cb != null) {
4517 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
4518 }
4519 }
4520 });
4521
4522 // Write all the logs to disk
4523 mHandler.post(new Runnable() {
4524 public void run() {
4525 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
4526 if (callbacks == cb && cb != null) {
4527 callbacks.dumpLogsToLocalData();
4528 }
4529 }
4530 });
4531 }
4532 }
4533
4534 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
4535 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
4536 PackageManager packageManager = context.getPackageManager();
4537 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
4538 widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
4539 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
4540 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
4541 Collections.sort(widgetsAndShortcuts,
4542 new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
4543 return widgetsAndShortcuts;
4544 }
4545
4546 private static boolean isPackageDisabled(PackageManager pm, String packageName) {
4547 try {
4548 PackageInfo pi = pm.getPackageInfo(packageName, 0);
4549 return !pi.applicationInfo.enabled;
4550 } catch (NameNotFoundException e) {
4551 // Fall through
4552 }
4553 return false;
4554 }
4555
4556 public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
4557 if (cn == null) {
4558 return false;
4559 }
4560 if (isPackageDisabled(pm, cn.getPackageName())) {
4561 return false;
4562 }
4563
4564 try {
4565 // Check the activity
4566 PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
4567 return (pm.getActivityInfo(cn, 0) != null);
4568 } catch (NameNotFoundException e) {
4569 return false;
4570 }
4571 }
4572
4573 /**
4574 * Make an ShortcutInfo object for a restored application or shortcut item that points
4575 * to a package that is not yet installed on the system.
4576 */
4577 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) {
4578 final ShortcutInfo info = new ShortcutInfo();
4579 info.usingFallbackIcon = true;
4580 info.setIcon(getFallbackIcon());
4581 if (cursor != null) {
4582 info.title = cursor.getString(titleIndex);
4583 } else {
4584 info.title = "";
4585 }
4586 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
4587 info.restoredIntent = intent;
4588 return info;
4589 }
4590
4591 /**
4592 * Make an Intent object for a restored application or shortcut item that points
4593 * to the market page for the item.
4594 */
4595 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
4596 final boolean debug = false;
4597 ComponentName componentName = intent.getComponent();
4598 Intent marketIntent = new Intent(Intent.ACTION_VIEW);
4599 Uri marketUri = new Uri.Builder()
4600 .scheme("market")
4601 .authority("details")
4602 .appendQueryParameter("id", componentName.getPackageName())
4603 .build();
4604 if (debug) Log.d(TAG, "manufactured intent uri: " + marketUri.toString());
4605 marketIntent.setData(marketUri);
4606 return marketIntent;
4607 }
4608
4609 /**
4610 * This is called from the code that adds shortcuts from the intent receiver. This
4611 * doesn't have a Cursor, but
4612 */
4613 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
4614 return getShortcutInfo(manager, intent, context, null, -1, -1, null);
4615 }
4616
4617 /**
4618 * Make an ShortcutInfo object for a shortcut that is an application.
4619 *
4620 * If c is not null, then it will be used to fill in missing data like the title and icon.
4621 */
4622 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
4623 Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
4624 ComponentName componentName = intent.getComponent();
4625 final ShortcutInfo info = new ShortcutInfo();
4626 if (componentName != null && !isValidPackageComponent(manager, componentName)) {
4627 Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
4628 return null;
4629 } else {
4630 try {
4631 PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
4632 info.initFlagsAndFirstInstallTime(pi);
4633 } catch (NameNotFoundException e) {
4634 Log.d(TAG, "getPackInfo failed for package " +
4635 componentName.getPackageName());
4636 }
4637 }
4638
4639 // TODO: See if the PackageManager knows about this case. If it doesn't
4640 // then return null & delete this.
4641
4642 // the resource -- This may implicitly give us back the fallback icon,
4643 // but don't worry about that. All we're doing with usingFallbackIcon is
4644 // to avoid saving lots of copies of that in the database, and most apps
4645 // have icons anyway.
4646
4647 // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
4648 // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
4649 // via resolveActivity().
4650 Bitmap icon = null;
4651 ResolveInfo resolveInfo = null;
4652 ComponentName oldComponent = intent.getComponent();
4653 Intent newIntent = new Intent(intent.getAction(), null);
4654 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
4655 newIntent.setPackage(oldComponent.getPackageName());
4656 List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
4657 for (ResolveInfo i : infos) {
4658 ComponentName cn = new ComponentName(i.activityInfo.packageName,
4659 i.activityInfo.name);
4660 if (cn.equals(oldComponent)) {
4661 resolveInfo = i;
4662 }
4663 }
4664 if (resolveInfo == null) {
4665 resolveInfo = manager.resolveActivity(intent, 0);
4666 }
4667 if (resolveInfo != null) {
4668 icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
4669 }
4670 // the db
4671 if (icon == null) {
4672 if (c != null) {
4673 icon = getIconFromCursor(c, iconIndex, context);
4674 }
4675 }
4676 // the fallback icon
4677 if (icon == null) {
4678 icon = getFallbackIcon();
4679 info.usingFallbackIcon = true;
4680 }
4681 info.setIcon(icon);
4682
4683 // from the resource
4684 if (resolveInfo != null) {
4685 ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
4686 if (labelCache != null && labelCache.containsKey(key)) {
4687 info.title = labelCache.get(key);
4688 } else {
4689 info.title = resolveInfo.activityInfo.loadLabel(manager);
4690 if (labelCache != null) {
4691 labelCache.put(key, info.title);
4692 }
4693 }
4694 }
4695 // from the db
4696 if (info.title == null) {
4697 if (c != null) {
4698 info.title = c.getString(titleIndex);
4699 }
4700 }
4701 // fall back to the class name of the activity
4702 if (info.title == null) {
4703 info.title = componentName.getClassName();
4704 }
4705 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
4706 return info;
4707 }
4708
4709 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
4710 ItemInfoFilter f) {
4711 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
4712 for (ItemInfo i : infos) {
4713 if (i instanceof ShortcutInfo) {
4714 ShortcutInfo info = (ShortcutInfo) i;
4715 ComponentName cn = info.intent.getComponent();
4716 if (cn != null && f.filterItem(null, info, cn)) {
4717 filtered.add(info);
4718 }
4719 } else if (i instanceof FolderInfo) {
4720 FolderInfo info = (FolderInfo) i;
4721 for (ShortcutInfo s : info.contents) {
4722 ComponentName cn = s.intent.getComponent();
4723 if (cn != null && f.filterItem(info, s, cn)) {
4724 filtered.add(s);
4725 }
4726 }
4727 } else if (i instanceof LauncherAppWidgetInfo) {
4728 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
4729 ComponentName cn = info.providerName;
4730 if (cn != null && f.filterItem(null, info, cn)) {
4731 filtered.add(info);
4732 }
4733 }
4734 }
4735 return new ArrayList<ItemInfo>(filtered);
4736 }
4737
4738 private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
4739 ItemInfoFilter filter = new ItemInfoFilter() {
4740 @Override
4741 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
4742 return cn.getPackageName().equals(pn);
4743 }
4744 };
4745 return filterItemInfos(sBgItemsIdMap.values(), filter);
4746 }
4747
4748 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
4749 ItemInfoFilter filter = new ItemInfoFilter() {
4750 @Override
4751 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
4752 return cn.equals(cname);
4753 }
4754 };
4755 return filterItemInfos(sBgItemsIdMap.values(), filter);
4756 }
4757
4758 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
4759 if (i instanceof ShortcutInfo) {
4760 ShortcutInfo info = (ShortcutInfo) i;
4761 // We need to check for ACTION_MAIN otherwise getComponent() might
4762 // return null for some shortcuts (for instance, for shortcuts to
4763 // web pages.)
4764 Intent intent = info.intent;
4765 ComponentName name = intent.getComponent();
4766 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
4767 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
4768 return true;
4769 }
4770 // placeholder shortcuts get special treatment, let them through too.
4771 if (info.getRestoredIntent() != null) {
4772 return true;
4773 }
4774 }
4775 return false;
4776 }
4777
4778 /**
4779 * Make an ShortcutInfo object for a shortcut that isn't an application.
4780 */
4781 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
4782 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
4783 int titleIndex) {
4784
4785 Bitmap icon = null;
4786 final ShortcutInfo info = new ShortcutInfo();
4787 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
4788
4789 // TODO: If there's an explicit component and we can't install that, delete it.
4790
4791 info.title = c.getString(titleIndex);
4792
4793 int iconType = c.getInt(iconTypeIndex);
4794 switch (iconType) {
4795 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
4796 String packageName = c.getString(iconPackageIndex);
4797 String resourceName = c.getString(iconResourceIndex);
4798 PackageManager packageManager = context.getPackageManager();
4799 info.customIcon = false;
4800 // the resource
4801 try {
4802 Resources resources = packageManager.getResourcesForApplication(packageName);
4803 if (resources != null) {
4804 final int id = resources.getIdentifier(resourceName, null, null);
4805 icon = Utilities.createIconBitmap(
4806 mIconCache.getFullResIcon(resources, id), context);
4807 }
4808 } catch (Exception e) {
4809 // drop this. we have other places to look for icons
4810 }
4811 // the db
4812 if (icon == null) {
4813 icon = getIconFromCursor(c, iconIndex, context);
4814 }
4815 // the fallback icon
4816 if (icon == null) {
4817 icon = getFallbackIcon();
4818 info.usingFallbackIcon = true;
4819 }
4820 break;
4821 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
4822 icon = getIconFromCursor(c, iconIndex, context);
4823 if (icon == null) {
4824 icon = getFallbackIcon();
4825 info.customIcon = false;
4826 info.usingFallbackIcon = true;
4827 } else {
4828 info.customIcon = true;
4829 }
4830 break;
4831 default:
4832 icon = getFallbackIcon();
4833 info.usingFallbackIcon = true;
4834 info.customIcon = false;
4835 break;
4836 }
4837 info.setIcon(icon);
4838 return info;
4839 }
4840
4841 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
4842 @SuppressWarnings("all") // suppress dead code warning
4843 final boolean debug = false;
4844 if (debug) {
4845 Log.d(TAG, "getIconFromCursor app="
4846 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
4847 }
4848 byte[] data = c.getBlob(iconIndex);
4849 try {
4850 return Utilities.createIconBitmap(
4851 BitmapFactory.decodeByteArray(data, 0, data.length), context);
4852 } catch (Exception e) {
4853 return null;
4854 }
4855 }
4856
4857 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
4858 int cellX, int cellY, boolean notify) {
4859 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
4860 if (info == null) {
4861 return null;
4862 }
4863 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
4864
4865 return info;
4866 }
4867
4868 /**
4869 * Attempts to find an AppWidgetProviderInfo that matches the given component.
4870 */
4871 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
4872 ComponentName component) {
4873 List<AppWidgetProviderInfo> widgets =
4874 AppWidgetManager.getInstance(context).getInstalledProviders();
4875 for (AppWidgetProviderInfo info : widgets) {
4876 if (info.provider.equals(component)) {
4877 return info;
4878 }
4879 }
4880 return null;
4881 }
4882
4883 /**
4884 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
4885 */
4886 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
4887 final PackageManager packageManager = context.getPackageManager();
4888 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
4889 new ArrayList<WidgetMimeTypeHandlerData>();
4890
4891 final Intent supportsIntent =
4892 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
4893 supportsIntent.setType(mimeType);
4894
4895 // Create a set of widget configuration components that we can test against
4896 final List<AppWidgetProviderInfo> widgets =
4897 AppWidgetManager.getInstance(context).getInstalledProviders();
4898 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
4899 new HashMap<ComponentName, AppWidgetProviderInfo>();
4900 for (AppWidgetProviderInfo info : widgets) {
4901 configurationComponentToWidget.put(info.configure, info);
4902 }
4903
4904 // Run through each of the intents that can handle this type of clip data, and cross
4905 // reference them with the components that are actual configuration components
4906 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
4907 PackageManager.MATCH_DEFAULT_ONLY);
4908 for (ResolveInfo info : activities) {
4909 final ActivityInfo activityInfo = info.activityInfo;
4910 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
4911 activityInfo.name);
4912 if (configurationComponentToWidget.containsKey(infoComponent)) {
4913 supportedConfigurationActivities.add(
4914 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
4915 configurationComponentToWidget.get(infoComponent)));
4916 }
4917 }
4918 return supportedConfigurationActivities;
4919 }
4920
4921 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
4922 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
4923 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
4924 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
4925
4926 if (intent == null) {
4927 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
4928 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
4929 return null;
4930 }
4931
4932 Bitmap icon = null;
4933 boolean customIcon = false;
4934 ShortcutIconResource iconResource = null;
4935
4936 if (bitmap != null && bitmap instanceof Bitmap) {
4937 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
4938 customIcon = true;
4939 } else {
4940 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
4941 if (extra != null && extra instanceof ShortcutIconResource) {
4942 try {
4943 iconResource = (ShortcutIconResource) extra;
4944 final PackageManager packageManager = context.getPackageManager();
4945 Resources resources = packageManager.getResourcesForApplication(
4946 iconResource.packageName);
4947 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
4948 icon = Utilities.createIconBitmap(
4949 mIconCache.getFullResIcon(resources, id), context);
4950 } catch (Exception e) {
4951 Log.w(TAG, "Could not load shortcut icon: " + extra);
4952 }
4953 }
4954 }
4955
4956 final ShortcutInfo info = new ShortcutInfo();
4957
4958 if (icon == null) {
4959 if (fallbackIcon != null) {
4960 icon = fallbackIcon;
4961 } else {
4962 icon = getFallbackIcon();
4963 =======
4964 >>>>>>> GitAnalyzerPlus_theirs
4965 return info;
4966 }
4967
4968 /**
4969 * Make an Intent object for a restored application or shortcut item that points
4970 * to the market page for the item.
4971 */
4972 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
4973 ComponentName componentName = intent.getComponent();
4974 return getMarketIntent(componentName.getPackageName());
4975 }
4976
4977 static Intent getMarketIntent(String packageName) {
4978 return new Intent(Intent.ACTION_VIEW)
4979 .setData(new Uri.Builder()
4980 .scheme("market")
4981 .authority("details")
4982 <<<<<<< GitAnalyzerPlus_ours
4983 .appendQueryParameter("id", packageName)
4984 .build());
4985 ||||||| GitAnalyzerPlus_base
4986 public void run() {
4987 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
4988 if (callbacks == cb && cb != null) {
4989 callbacks.dumpLogsToLocalData();
4990 }
4991 }
4992 });
4993 }
4994 }
4995
4996 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
4997 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
4998 PackageManager packageManager = context.getPackageManager();
4999 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
5000 widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
5001 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
5002 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
5003 Collections.sort(widgetsAndShortcuts,
5004 new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
5005 return widgetsAndShortcuts;
5006 }
5007
5008 private static boolean isPackageDisabled(PackageManager pm, String packageName) {
5009 try {
5010 PackageInfo pi = pm.getPackageInfo(packageName, 0);
5011 return !pi.applicationInfo.enabled;
5012 } catch (NameNotFoundException e) {
5013 // Fall through
5014 }
5015 return false;
5016 }
5017
5018 public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
5019 if (cn == null) {
5020 return false;
5021 }
5022 if (isPackageDisabled(pm, cn.getPackageName())) {
5023 return false;
5024 }
5025
5026 try {
5027 // Check the activity
5028 PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
5029 return (pm.getActivityInfo(cn, 0) != null);
5030 } catch (NameNotFoundException e) {
5031 return false;
5032 }
5033 }
5034
5035 /**
5036 * Make an ShortcutInfo object for a restored application or shortcut item that points
5037 * to a package that is not yet installed on the system.
5038 */
5039 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) {
5040 final ShortcutInfo info = new ShortcutInfo();
5041 info.usingFallbackIcon = true;
5042 info.setIcon(getFallbackIcon());
5043 if (cursor != null) {
5044 info.title = cursor.getString(titleIndex);
5045 } else {
5046 info.title = "";
5047 }
5048 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
5049 info.restoredIntent = intent;
5050 return info;
5051 }
5052
5053 /**
5054 * Make an Intent object for a restored application or shortcut item that points
5055 * to the market page for the item.
5056 */
5057 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
5058 final boolean debug = false;
5059 ComponentName componentName = intent.getComponent();
5060 Intent marketIntent = new Intent(Intent.ACTION_VIEW);
5061 Uri marketUri = new Uri.Builder()
5062 .scheme("market")
5063 .authority("details")
5064 .appendQueryParameter("id", componentName.getPackageName())
5065 .build();
5066 if (debug) Log.d(TAG, "manufactured intent uri: " + marketUri.toString());
5067 marketIntent.setData(marketUri);
5068 return marketIntent;
5069 }
5070
5071 /**
5072 * This is called from the code that adds shortcuts from the intent receiver. This
5073 * doesn't have a Cursor, but
5074 */
5075 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
5076 return getShortcutInfo(manager, intent, context, null, -1, -1, null);
5077 }
5078
5079 /**
5080 * Make an ShortcutInfo object for a shortcut that is an application.
5081 *
5082 * If c is not null, then it will be used to fill in missing data like the title and icon.
5083 */
5084 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
5085 Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
5086 ComponentName componentName = intent.getComponent();
5087 final ShortcutInfo info = new ShortcutInfo();
5088 if (componentName != null && !isValidPackageComponent(manager, componentName)) {
5089 Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
5090 return null;
5091 } else {
5092 try {
5093 PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
5094 info.initFlagsAndFirstInstallTime(pi);
5095 } catch (NameNotFoundException e) {
5096 Log.d(TAG, "getPackInfo failed for package " +
5097 componentName.getPackageName());
5098 }
5099 }
5100
5101 // TODO: See if the PackageManager knows about this case. If it doesn't
5102 // then return null & delete this.
5103
5104 // the resource -- This may implicitly give us back the fallback icon,
5105 // but don't worry about that. All we're doing with usingFallbackIcon is
5106 // to avoid saving lots of copies of that in the database, and most apps
5107 // have icons anyway.
5108
5109 // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
5110 // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
5111 // via resolveActivity().
5112 Bitmap icon = null;
5113 ResolveInfo resolveInfo = null;
5114 ComponentName oldComponent = intent.getComponent();
5115 Intent newIntent = new Intent(intent.getAction(), null);
5116 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
5117 newIntent.setPackage(oldComponent.getPackageName());
5118 List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
5119 for (ResolveInfo i : infos) {
5120 ComponentName cn = new ComponentName(i.activityInfo.packageName,
5121 i.activityInfo.name);
5122 if (cn.equals(oldComponent)) {
5123 resolveInfo = i;
5124 }
5125 }
5126 if (resolveInfo == null) {
5127 resolveInfo = manager.resolveActivity(intent, 0);
5128 }
5129 if (resolveInfo != null) {
5130 icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
5131 }
5132 // the db
5133 if (icon == null) {
5134 if (c != null) {
5135 icon = getIconFromCursor(c, iconIndex, context);
5136 }
5137 }
5138 // the fallback icon
5139 if (icon == null) {
5140 icon = getFallbackIcon();
5141 info.usingFallbackIcon = true;
5142 }
5143 info.setIcon(icon);
5144
5145 // from the resource
5146 if (resolveInfo != null) {
5147 ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
5148 if (labelCache != null && labelCache.containsKey(key)) {
5149 info.title = labelCache.get(key);
5150 } else {
5151 info.title = resolveInfo.activityInfo.loadLabel(manager);
5152 if (labelCache != null) {
5153 labelCache.put(key, info.title);
5154 }
5155 }
5156 }
5157 // from the db
5158 if (info.title == null) {
5159 if (c != null) {
5160 info.title = c.getString(titleIndex);
5161 }
5162 }
5163 // fall back to the class name of the activity
5164 if (info.title == null) {
5165 info.title = componentName.getClassName();
5166 }
5167 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
5168 return info;
5169 }
5170
5171 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
5172 ItemInfoFilter f) {
5173 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
5174 for (ItemInfo i : infos) {
5175 if (i instanceof ShortcutInfo) {
5176 ShortcutInfo info = (ShortcutInfo) i;
5177 ComponentName cn = info.intent.getComponent();
5178 if (cn != null && f.filterItem(null, info, cn)) {
5179 filtered.add(info);
5180 }
5181 } else if (i instanceof FolderInfo) {
5182 FolderInfo info = (FolderInfo) i;
5183 for (ShortcutInfo s : info.contents) {
5184 ComponentName cn = s.intent.getComponent();
5185 if (cn != null && f.filterItem(info, s, cn)) {
5186 filtered.add(s);
5187 }
5188 }
5189 } else if (i instanceof LauncherAppWidgetInfo) {
5190 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
5191 ComponentName cn = info.providerName;
5192 if (cn != null && f.filterItem(null, info, cn)) {
5193 filtered.add(info);
5194 }
5195 }
5196 }
5197 return new ArrayList<ItemInfo>(filtered);
5198 }
5199
5200 private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
5201 ItemInfoFilter filter = new ItemInfoFilter() {
5202 @Override
5203 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
5204 return cn.getPackageName().equals(pn);
5205 }
5206 };
5207 return filterItemInfos(sBgItemsIdMap.values(), filter);
5208 }
5209
5210 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
5211 ItemInfoFilter filter = new ItemInfoFilter() {
5212 @Override
5213 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
5214 return cn.equals(cname);
5215 }
5216 };
5217 return filterItemInfos(sBgItemsIdMap.values(), filter);
5218 }
5219
5220 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
5221 if (i instanceof ShortcutInfo) {
5222 ShortcutInfo info = (ShortcutInfo) i;
5223 // We need to check for ACTION_MAIN otherwise getComponent() might
5224 // return null for some shortcuts (for instance, for shortcuts to
5225 // web pages.)
5226 Intent intent = info.intent;
5227 ComponentName name = intent.getComponent();
5228 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
5229 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
5230 return true;
5231 }
5232 // placeholder shortcuts get special treatment, let them through too.
5233 if (info.getRestoredIntent() != null) {
5234 return true;
5235 }
5236 }
5237 return false;
5238 }
5239
5240 /**
5241 * Make an ShortcutInfo object for a shortcut that isn't an application.
5242 */
5243 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
5244 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
5245 int titleIndex) {
5246
5247 Bitmap icon = null;
5248 final ShortcutInfo info = new ShortcutInfo();
5249 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
5250
5251 // TODO: If there's an explicit component and we can't install that, delete it.
5252
5253 info.title = c.getString(titleIndex);
5254
5255 int iconType = c.getInt(iconTypeIndex);
5256 switch (iconType) {
5257 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
5258 String packageName = c.getString(iconPackageIndex);
5259 String resourceName = c.getString(iconResourceIndex);
5260 PackageManager packageManager = context.getPackageManager();
5261 info.customIcon = false;
5262 // the resource
5263 try {
5264 Resources resources = packageManager.getResourcesForApplication(packageName);
5265 if (resources != null) {
5266 final int id = resources.getIdentifier(resourceName, null, null);
5267 icon = Utilities.createIconBitmap(
5268 mIconCache.getFullResIcon(resources, id), context);
5269 }
5270 } catch (Exception e) {
5271 // drop this. we have other places to look for icons
5272 }
5273 // the db
5274 if (icon == null) {
5275 icon = getIconFromCursor(c, iconIndex, context);
5276 }
5277 // the fallback icon
5278 if (icon == null) {
5279 icon = getFallbackIcon();
5280 info.usingFallbackIcon = true;
5281 }
5282 break;
5283 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
5284 icon = getIconFromCursor(c, iconIndex, context);
5285 if (icon == null) {
5286 icon = getFallbackIcon();
5287 info.customIcon = false;
5288 info.usingFallbackIcon = true;
5289 } else {
5290 info.customIcon = true;
5291 }
5292 break;
5293 default:
5294 icon = getFallbackIcon();
5295 info.usingFallbackIcon = true;
5296 info.customIcon = false;
5297 break;
5298 }
5299 info.setIcon(icon);
5300 return info;
5301 }
5302
5303 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
5304 @SuppressWarnings("all") // suppress dead code warning
5305 final boolean debug = false;
5306 if (debug) {
5307 Log.d(TAG, "getIconFromCursor app="
5308 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
5309 }
5310 byte[] data = c.getBlob(iconIndex);
5311 try {
5312 return Utilities.createIconBitmap(
5313 BitmapFactory.decodeByteArray(data, 0, data.length), context);
5314 } catch (Exception e) {
5315 return null;
5316 }
5317 }
5318
5319 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
5320 int cellX, int cellY, boolean notify) {
5321 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
5322 if (info == null) {
5323 return null;
5324 }
5325 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
5326
5327 return info;
5328 }
5329
5330 /**
5331 * Attempts to find an AppWidgetProviderInfo that matches the given component.
5332 */
5333 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
5334 ComponentName component) {
5335 List<AppWidgetProviderInfo> widgets =
5336 AppWidgetManager.getInstance(context).getInstalledProviders();
5337 for (AppWidgetProviderInfo info : widgets) {
5338 if (info.provider.equals(component)) {
5339 return info;
5340 }
5341 }
5342 return null;
5343 }
5344
5345 /**
5346 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
5347 */
5348 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
5349 final PackageManager packageManager = context.getPackageManager();
5350 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
5351 new ArrayList<WidgetMimeTypeHandlerData>();
5352
5353 final Intent supportsIntent =
5354 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
5355 supportsIntent.setType(mimeType);
5356
5357 // Create a set of widget configuration components that we can test against
5358 final List<AppWidgetProviderInfo> widgets =
5359 AppWidgetManager.getInstance(context).getInstalledProviders();
5360 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
5361 new HashMap<ComponentName, AppWidgetProviderInfo>();
5362 for (AppWidgetProviderInfo info : widgets) {
5363 configurationComponentToWidget.put(info.configure, info);
5364 }
5365
5366 // Run through each of the intents that can handle this type of clip data, and cross
5367 // reference them with the components that are actual configuration components
5368 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
5369 PackageManager.MATCH_DEFAULT_ONLY);
5370 for (ResolveInfo info : activities) {
5371 final ActivityInfo activityInfo = info.activityInfo;
5372 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
5373 activityInfo.name);
5374 if (configurationComponentToWidget.containsKey(infoComponent)) {
5375 supportedConfigurationActivities.add(
5376 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
5377 configurationComponentToWidget.get(infoComponent)));
5378 }
5379 }
5380 return supportedConfigurationActivities;
5381 }
5382
5383 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
5384 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
5385 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
5386 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
5387
5388 if (intent == null) {
5389 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
5390 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
5391 return null;
5392 }
5393
5394 Bitmap icon = null;
5395 boolean customIcon = false;
5396 ShortcutIconResource iconResource = null;
5397
5398 if (bitmap != null && bitmap instanceof Bitmap) {
5399 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
5400 customIcon = true;
5401 } else {
5402 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
5403 if (extra != null && extra instanceof ShortcutIconResource) {
5404 try {
5405 iconResource = (ShortcutIconResource) extra;
5406 final PackageManager packageManager = context.getPackageManager();
5407 Resources resources = packageManager.getResourcesForApplication(
5408 iconResource.packageName);
5409 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
5410 icon = Utilities.createIconBitmap(
5411 mIconCache.getFullResIcon(resources, id), context);
5412 } catch (Exception e) {
5413 Log.w(TAG, "Could not load shortcut icon: " + extra);
5414 }
5415 }
5416 }
5417
5418 final ShortcutInfo info = new ShortcutInfo();
5419
5420 if (icon == null) {
5421 if (fallbackIcon != null) {
5422 icon = fallbackIcon;
5423 } else {
5424 icon = getFallbackIcon();
5425 info.usingFallbackIcon = true;
5426 }
5427 }
5428 info.setIcon(icon);
5429
5430 info.title = name;
5431 info.intent = intent;
5432 info.customIcon = customIcon;
5433 info.iconResource = iconResource;
5434
5435 return info;
5436 }
5437
5438 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
5439 int iconIndex) {
5440 // If apps can't be on SD, don't even bother.
5441 if (!mAppsCanBeOnRemoveableStorage) {
5442 return false;
5443 }
5444 =======
5445 .appendQueryParameter("id", componentName.getPackageName())
5446 .build();
5447 Log.d(TAG, "manufactured intent uri: " + marketUri.toString());
5448 marketIntent.setData(marketUri);
5449 return marketIntent;
5450 >>>>>>> GitAnalyzerPlus_theirs
5451 }
5452
5453 /**
5454 * This is called from the code that adds shortcuts from the intent receiver. This
5455 * doesn't have a Cursor, but
5456 */
5457 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
5458 UserHandleCompat user, Context context) {
5459 return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
5460 }
5461
5462 /**
5463 * Make an ShortcutInfo object for a shortcut that is an application.
5464 *
5465 * If c is not null, then it will be used to fill in missing data like the title and icon.
5466 */
5467 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
5468 UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
5469 HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
5470 if (user == null) {
5471 Log.d(TAG, "Null user found in getShortcutInfo");
5472 return null;
5473 }
5474
5475 ComponentName componentName = intent.getComponent();
5476 if (componentName == null) {
5477 Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
5478 return null;
5479 }
5480
5481 Intent newIntent = new Intent(intent.getAction(), null);
5482 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
5483 newIntent.setComponent(componentName);
5484 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
5485 if ((lai == null) && !allowMissingTarget) {
5486 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
5487 return null;
5488 }
5489
5490 final ShortcutInfo info = new ShortcutInfo();
5491
5492 // the resource -- This may implicitly give us back the fallback icon,
5493 // but don't worry about that. All we're doing with usingFallbackIcon is
5494 // to avoid saving lots of copies of that in the database, and most apps
5495 // have icons anyway.
5496 Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
5497
5498 // the db
5499 if (icon == null) {
5500 if (c != null) {
5501 icon = getIconFromCursor(c, iconIndex, context);
5502 }
5503 }
5504 // the fallback icon
5505 if (icon == null) {
5506 icon = mIconCache.getDefaultIcon(user);
5507 info.usingFallbackIcon = true;
5508 }
5509 info.setIcon(icon);
5510
5511 // From the cache.
5512 if (labelCache != null) {
5513 info.title = labelCache.get(componentName);
5514 }
5515
5516 // from the resource
5517 if (info.title == null && lai != null) {
5518 info.title = lai.getLabel();
5519 if (labelCache != null) {
5520 labelCache.put(componentName, info.title);
5521 }
5522 }
5523 // from the db
5524 if (info.title == null) {
5525 if (c != null) {
5526 info.title = c.getString(titleIndex);
5527 }
5528 }
5529 // fall back to the class name of the activity
5530 if (info.title == null) {
5531 info.title = componentName.getClassName();
5532 }
5533 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
5534 info.user = user;
5535 info.contentDescription = mUserManager.getBadgedLabelForUser(
5536 info.title.toString(), info.user);
5537 return info;
5538 }
5539
5540 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
5541 ItemInfoFilter f) {
5542 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
5543 for (ItemInfo i : infos) {
5544 if (i instanceof ShortcutInfo) {
5545 ShortcutInfo info = (ShortcutInfo) i;
5546 ComponentName cn = info.getTargetComponent();
5547 if (cn != null && f.filterItem(null, info, cn)) {
5548 filtered.add(info);
5549 }
5550 } else if (i instanceof FolderInfo) {
5551 FolderInfo info = (FolderInfo) i;
5552 for (ShortcutInfo s : info.contents) {
5553 ComponentName cn = s.getTargetComponent();
5554 if (cn != null && f.filterItem(info, s, cn)) {
5555 filtered.add(s);
5556 }
5557 }
5558 } else if (i instanceof LauncherAppWidgetInfo) {
5559 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
5560 ComponentName cn = info.providerName;
5561 if (cn != null && f.filterItem(null, info, cn)) {
5562 filtered.add(info);
5563 }
5564 }
5565 }
5566 return new ArrayList<ItemInfo>(filtered);
5567 }
5568
5569 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
5570 final UserHandleCompat user) {
5571 ItemInfoFilter filter = new ItemInfoFilter() {
5572 @Override
5573 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
5574 if (info.user == null) {
5575 return cn.equals(cname);
5576 } else {
5577 return cn.equals(cname) && info.user.equals(user);
5578 }
5579 }
5580 };
5581 return filterItemInfos(sBgItemsIdMap.values(), filter);
5582 }
5583
5584 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
5585 if (i instanceof ShortcutInfo) {
5586 ShortcutInfo info = (ShortcutInfo) i;
5587 // We need to check for ACTION_MAIN otherwise getComponent() might
5588 // return null for some shortcuts (for instance, for shortcuts to
5589 // web pages.)
5590 Intent intent = info.intent;
5591 ComponentName name = intent.getComponent();
5592 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
5593 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
5594 return true;
5595 }
5596 <<<<<<< GitAnalyzerPlus_ours
5597 // placeholder shortcuts get special treatment, let them through too.
5598 if (info.isPromise()) {
5599 return true;
5600 }
5601 ||||||| GitAnalyzerPlus_base
5602 }
5603 }
5604 }
5605 // from the db
5606 if (info.title == null) {
5607 if (c != null) {
5608 info.title = c.getString(titleIndex);
5609 }
5610 }
5611 // fall back to the class name of the activity
5612 if (info.title == null) {
5613 info.title = componentName.getClassName();
5614 }
5615 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
5616 return info;
5617 }
5618
5619 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
5620 ItemInfoFilter f) {
5621 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
5622 for (ItemInfo i : infos) {
5623 if (i instanceof ShortcutInfo) {
5624 ShortcutInfo info = (ShortcutInfo) i;
5625 ComponentName cn = info.intent.getComponent();
5626 if (cn != null && f.filterItem(null, info, cn)) {
5627 filtered.add(info);
5628 }
5629 } else if (i instanceof FolderInfo) {
5630 FolderInfo info = (FolderInfo) i;
5631 for (ShortcutInfo s : info.contents) {
5632 ComponentName cn = s.intent.getComponent();
5633 if (cn != null && f.filterItem(info, s, cn)) {
5634 filtered.add(s);
5635 }
5636 }
5637 } else if (i instanceof LauncherAppWidgetInfo) {
5638 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
5639 ComponentName cn = info.providerName;
5640 if (cn != null && f.filterItem(null, info, cn)) {
5641 filtered.add(info);
5642 }
5643 }
5644 }
5645 return new ArrayList<ItemInfo>(filtered);
5646 }
5647
5648 private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
5649 ItemInfoFilter filter = new ItemInfoFilter() {
5650 @Override
5651 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
5652 return cn.getPackageName().equals(pn);
5653 }
5654 };
5655 return filterItemInfos(sBgItemsIdMap.values(), filter);
5656 }
5657
5658 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
5659 ItemInfoFilter filter = new ItemInfoFilter() {
5660 @Override
5661 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
5662 return cn.equals(cname);
5663 }
5664 };
5665 return filterItemInfos(sBgItemsIdMap.values(), filter);
5666 }
5667
5668 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
5669 if (i instanceof ShortcutInfo) {
5670 ShortcutInfo info = (ShortcutInfo) i;
5671 // We need to check for ACTION_MAIN otherwise getComponent() might
5672 // return null for some shortcuts (for instance, for shortcuts to
5673 // web pages.)
5674 Intent intent = info.intent;
5675 ComponentName name = intent.getComponent();
5676 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
5677 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
5678 return true;
5679 }
5680 // placeholder shortcuts get special treatment, let them through too.
5681 if (info.getRestoredIntent() != null) {
5682 return true;
5683 }
5684 }
5685 return false;
5686 }
5687
5688 /**
5689 * Make an ShortcutInfo object for a shortcut that isn't an application.
5690 */
5691 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
5692 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
5693 int titleIndex) {
5694
5695 Bitmap icon = null;
5696 final ShortcutInfo info = new ShortcutInfo();
5697 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
5698
5699 // TODO: If there's an explicit component and we can't install that, delete it.
5700
5701 info.title = c.getString(titleIndex);
5702
5703 int iconType = c.getInt(iconTypeIndex);
5704 switch (iconType) {
5705 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
5706 String packageName = c.getString(iconPackageIndex);
5707 String resourceName = c.getString(iconResourceIndex);
5708 PackageManager packageManager = context.getPackageManager();
5709 info.customIcon = false;
5710 // the resource
5711 try {
5712 Resources resources = packageManager.getResourcesForApplication(packageName);
5713 if (resources != null) {
5714 final int id = resources.getIdentifier(resourceName, null, null);
5715 icon = Utilities.createIconBitmap(
5716 mIconCache.getFullResIcon(resources, id), context);
5717 }
5718 } catch (Exception e) {
5719 // drop this. we have other places to look for icons
5720 }
5721 // the db
5722 if (icon == null) {
5723 icon = getIconFromCursor(c, iconIndex, context);
5724 }
5725 // the fallback icon
5726 if (icon == null) {
5727 icon = getFallbackIcon();
5728 info.usingFallbackIcon = true;
5729 }
5730 break;
5731 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
5732 icon = getIconFromCursor(c, iconIndex, context);
5733 if (icon == null) {
5734 icon = getFallbackIcon();
5735 info.customIcon = false;
5736 info.usingFallbackIcon = true;
5737 } else {
5738 info.customIcon = true;
5739 }
5740 break;
5741 default:
5742 icon = getFallbackIcon();
5743 info.usingFallbackIcon = true;
5744 info.customIcon = false;
5745 break;
5746 }
5747 info.setIcon(icon);
5748 return info;
5749 }
5750
5751 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
5752 @SuppressWarnings("all") // suppress dead code warning
5753 final boolean debug = false;
5754 if (debug) {
5755 Log.d(TAG, "getIconFromCursor app="
5756 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
5757 }
5758 byte[] data = c.getBlob(iconIndex);
5759 try {
5760 return Utilities.createIconBitmap(
5761 BitmapFactory.decodeByteArray(data, 0, data.length), context);
5762 } catch (Exception e) {
5763 return null;
5764 }
5765 }
5766
5767 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
5768 int cellX, int cellY, boolean notify) {
5769 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
5770 if (info == null) {
5771 return null;
5772 }
5773 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
5774
5775 return info;
5776 }
5777
5778 /**
5779 * Attempts to find an AppWidgetProviderInfo that matches the given component.
5780 */
5781 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
5782 ComponentName component) {
5783 List<AppWidgetProviderInfo> widgets =
5784 AppWidgetManager.getInstance(context).getInstalledProviders();
5785 for (AppWidgetProviderInfo info : widgets) {
5786 if (info.provider.equals(component)) {
5787 return info;
5788 }
5789 }
5790 return null;
5791 }
5792
5793 /**
5794 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
5795 */
5796 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
5797 final PackageManager packageManager = context.getPackageManager();
5798 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
5799 new ArrayList<WidgetMimeTypeHandlerData>();
5800
5801 final Intent supportsIntent =
5802 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
5803 supportsIntent.setType(mimeType);
5804
5805 // Create a set of widget configuration components that we can test against
5806 final List<AppWidgetProviderInfo> widgets =
5807 AppWidgetManager.getInstance(context).getInstalledProviders();
5808 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
5809 new HashMap<ComponentName, AppWidgetProviderInfo>();
5810 for (AppWidgetProviderInfo info : widgets) {
5811 configurationComponentToWidget.put(info.configure, info);
5812 }
5813
5814 // Run through each of the intents that can handle this type of clip data, and cross
5815 // reference them with the components that are actual configuration components
5816 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
5817 PackageManager.MATCH_DEFAULT_ONLY);
5818 for (ResolveInfo info : activities) {
5819 final ActivityInfo activityInfo = info.activityInfo;
5820 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
5821 activityInfo.name);
5822 if (configurationComponentToWidget.containsKey(infoComponent)) {
5823 supportedConfigurationActivities.add(
5824 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
5825 configurationComponentToWidget.get(infoComponent)));
5826 }
5827 }
5828 return supportedConfigurationActivities;
5829 }
5830
5831 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
5832 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
5833 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
5834 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
5835
5836 if (intent == null) {
5837 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
5838 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
5839 return null;
5840 }
5841
5842 Bitmap icon = null;
5843 boolean customIcon = false;
5844 ShortcutIconResource iconResource = null;
5845
5846 if (bitmap != null && bitmap instanceof Bitmap) {
5847 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
5848 customIcon = true;
5849 } else {
5850 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
5851 if (extra != null && extra instanceof ShortcutIconResource) {
5852 try {
5853 iconResource = (ShortcutIconResource) extra;
5854 final PackageManager packageManager = context.getPackageManager();
5855 Resources resources = packageManager.getResourcesForApplication(
5856 iconResource.packageName);
5857 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
5858 icon = Utilities.createIconBitmap(
5859 mIconCache.getFullResIcon(resources, id), context);
5860 } catch (Exception e) {
5861 Log.w(TAG, "Could not load shortcut icon: " + extra);
5862 }
5863 }
5864 }
5865
5866 final ShortcutInfo info = new ShortcutInfo();
5867
5868 if (icon == null) {
5869 if (fallbackIcon != null) {
5870 icon = fallbackIcon;
5871 } else {
5872 icon = getFallbackIcon();
5873 info.usingFallbackIcon = true;
5874 }
5875 }
5876 info.setIcon(icon);
5877
5878 info.title = name;
5879 info.intent = intent;
5880 info.customIcon = customIcon;
5881 info.iconResource = iconResource;
5882
5883 return info;
5884 }
5885
5886 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
5887 int iconIndex) {
5888 // If apps can't be on SD, don't even bother.
5889 if (!mAppsCanBeOnRemoveableStorage) {
5890 return false;
5891 }
5892 // If this icon doesn't have a custom icon, check to see
5893 // what's stored in the DB, and if it doesn't match what
5894 // we're going to show, store what we are going to show back
5895 // into the DB. We do this so when we're loading, if the
5896 // package manager can't find an icon (for example because
5897 // the app is on SD) then we can use that instead.
5898 if (!info.customIcon && !info.usingFallbackIcon) {
5899 cache.put(info, c.getBlob(iconIndex));
5900 return true;
5901 }
5902 return false;
5903 }
5904 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
5905 boolean needSave = false;
5906 try {
5907 if (data != null) {
5908 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
5909 Bitmap loaded = info.getIcon(mIconCache);
5910 needSave = !saved.sameAs(loaded);
5911 } else {
5912 needSave = true;
5913 }
5914 } catch (Exception e) {
5915 needSave = true;
5916 }
5917 if (needSave) {
5918 Log.d(TAG, "going to save icon bitmap for info=" + info);
5919 // This is slower than is ideal, but this only happens once
5920 // or when the app is updated with a new icon.
5921 updateItemInDatabase(context, info);
5922 }
5923 }
5924
5925 /**
5926 * Return an existing FolderInfo object if we have encountered this ID previously,
5927 * or make a new one.
5928 */
5929 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
5930 // See if a placeholder was created for us already
5931 FolderInfo folderInfo = folders.get(id);
5932 if (folderInfo == null) {
5933 // No placeholder -- create a new instance
5934 folderInfo = new FolderInfo();
5935 folders.put(id, folderInfo);
5936 }
5937 return folderInfo;
5938 }
5939
5940 public static final Comparator<AppInfo> getAppNameComparator() {
5941 final Collator collator = Collator.getInstance();
5942 return new Comparator<AppInfo>() {
5943 public final int compare(AppInfo a, AppInfo b) {
5944 int result = collator.compare(a.title.toString().trim(),
5945 b.title.toString().trim());
5946 if (result == 0) {
5947 result = a.componentName.compareTo(b.componentName);
5948 }
5949 return result;
5950 }
5951 };
5952 }
5953 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
5954 = new Comparator<AppInfo>() {
5955 public final int compare(AppInfo a, AppInfo b) {
5956 if (a.firstInstallTime < b.firstInstallTime) return 1;
5957 if (a.firstInstallTime > b.firstInstallTime) return -1;
5958 return 0;
5959 }
5960 };
5961 public static final Comparator<AppWidgetProviderInfo> getWidgetNameComparator() {
5962 final Collator collator = Collator.getInstance();
5963 return new Comparator<AppWidgetProviderInfo>() {
5964 public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) {
5965 return collator.compare(a.label.toString().trim(), b.label.toString().trim());
5966 }
5967 };
5968 }
5969 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
5970 if (info.activityInfo != null) {
5971 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
5972 } else {
5973 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
5974 }
5975 }
5976 public static class ShortcutNameComparator implements Comparator<ResolveInfo> {
5977 private Collator mCollator;
5978 private PackageManager mPackageManager;
5979 private HashMap<Object, CharSequence> mLabelCache;
5980 ShortcutNameComparator(PackageManager pm) {
5981 mPackageManager = pm;
5982 mLabelCache = new HashMap<Object, CharSequence>();
5983 mCollator = Collator.getInstance();
5984 }
5985 ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) {
5986 mPackageManager = pm;
5987 mLabelCache = labelCache;
5988 mCollator = Collator.getInstance();
5989 }
5990 public final int compare(ResolveInfo a, ResolveInfo b) {
5991 CharSequence labelA, labelB;
5992 ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a);
5993 ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b);
5994 if (mLabelCache.containsKey(keyA)) {
5995 labelA = mLabelCache.get(keyA);
5996 } else {
5997 labelA = a.loadLabel(mPackageManager).toString().trim();
5998
5999 mLabelCache.put(keyA, labelA);
6000 }
6001 if (mLabelCache.containsKey(keyB)) {
6002 labelB = mLabelCache.get(keyB);
6003 } else {
6004 labelB = b.loadLabel(mPackageManager).toString().trim();
6005
6006 mLabelCache.put(keyB, labelB);
6007 }
6008 return mCollator.compare(labelA, labelB);
6009 }
6010 };
6011 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
6012 private Collator mCollator;
6013 private PackageManager mPackageManager;
6014 private HashMap<Object, String> mLabelCache;
6015 WidgetAndShortcutNameComparator(PackageManager pm) {
6016 mPackageManager = pm;
6017 mLabelCache = new HashMap<Object, String>();
6018 mCollator = Collator.getInstance();
6019 }
6020 public final int compare(Object a, Object b) {
6021 String labelA, labelB;
6022 if (mLabelCache.containsKey(a)) {
6023 labelA = mLabelCache.get(a);
6024 } else {
6025 labelA = (a instanceof AppWidgetProviderInfo) ?
6026 ((AppWidgetProviderInfo) a).label :
6027 ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
6028 mLabelCache.put(a, labelA);
6029 }
6030 if (mLabelCache.containsKey(b)) {
6031 labelB = mLabelCache.get(b);
6032 } else {
6033 labelB = (b instanceof AppWidgetProviderInfo) ?
6034 ((AppWidgetProviderInfo) b).label :
6035 ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
6036 mLabelCache.put(b, labelB);
6037 }
6038 return mCollator.compare(labelA, labelB);
6039 }
6040 };
6041 =======
6042 >>>>>>> GitAnalyzerPlus_theirs
6043 }
6044 return false;
6045 }
6046
6047 /**
6048 * Make an ShortcutInfo object for a shortcut that isn't an application.
6049 */
6050 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
6051 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
6052 int titleIndex) {
6053
6054 Bitmap icon = null;
6055 final ShortcutInfo info = new ShortcutInfo();
6056 // Non-app shortcuts are only supported for current user.
6057 info.user = UserHandleCompat.myUserHandle();
6058 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
6059
6060 // TODO: If there's an explicit component and we can't install that, delete it.
6061
6062 info.title = c.getString(titleIndex);
6063
6064 int iconType = c.getInt(iconTypeIndex);
6065 switch (iconType) {
6066 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
6067 String packageName = c.getString(iconPackageIndex);
6068 String resourceName = c.getString(iconResourceIndex);
6069 PackageManager packageManager = context.getPackageManager();
6070 info.customIcon = false;
6071 // the resource
6072 try {
6073 Resources resources = packageManager.getResourcesForApplication(packageName);
6074 if (resources != null) {
6075 final int id = resources.getIdentifier(resourceName, null, null);
6076 icon = Utilities.createIconBitmap(
6077 mIconCache.getFullResIcon(resources, id), context);
6078 }
6079 } catch (Exception e) {
6080 // drop this. we have other places to look for icons
6081 }
6082 // the db
6083 if (icon == null) {
6084 icon = getIconFromCursor(c, iconIndex, context);
6085 }
6086 // the fallback icon
6087 if (icon == null) {
6088 icon = mIconCache.getDefaultIcon(info.user);
6089 info.usingFallbackIcon = true;
6090 }
6091 break;
6092 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
6093 icon = getIconFromCursor(c, iconIndex, context);
6094 if (icon == null) {
6095 icon = mIconCache.getDefaultIcon(info.user);
6096 info.customIcon = false;
6097 info.usingFallbackIcon = true;
6098 } else {
6099 info.customIcon = true;
6100 }
6101 break;
6102 default:
6103 icon = mIconCache.getDefaultIcon(info.user);
6104 info.usingFallbackIcon = true;
6105 info.customIcon = false;
6106 break;
6107 }
6108 info.setIcon(icon);
6109 return info;
6110 }
6111
6112 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
6113 @SuppressWarnings("all") // suppress dead code warning
6114 final boolean debug = false;
6115 if (debug) {
6116 Log.d(TAG, "getIconFromCursor app="
6117 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
6118 }
6119 byte[] data = c.getBlob(iconIndex);
6120 try {
6121 return Utilities.createIconBitmap(
6122 BitmapFactory.decodeByteArray(data, 0, data.length), context);
6123 } catch (Exception e) {
6124 return null;
6125 }
6126 }
6127
6128 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
6129 int cellX, int cellY, boolean notify) {
6130 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
6131 if (info == null) {
6132 return null;
6133 }
6134 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
6135
6136 return info;
6137 }
6138
6139 /**
6140 * Attempts to find an AppWidgetProviderInfo that matches the given component.
6141 */
6142 static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
6143 ComponentName component) {
6144 List<AppWidgetProviderInfo> widgets =
6145 AppWidgetManager.getInstance(context).getInstalledProviders();
6146 for (AppWidgetProviderInfo info : widgets) {
6147 if (info.provider.equals(component)) {
6148 return info;
6149 }
6150 }
6151 return null;
6152 }
6153
6154 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
6155 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
6156 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
6157 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
6158
6159 if (intent == null) {
6160 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
6161 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
6162 return null;
6163 }
6164
6165 Bitmap icon = null;
6166 boolean customIcon = false;
6167 ShortcutIconResource iconResource = null;
6168
6169 if (bitmap != null && bitmap instanceof Bitmap) {
6170 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
6171 customIcon = true;
6172 } else {
6173 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
6174 if (extra != null && extra instanceof ShortcutIconResource) {
6175 try {
6176 iconResource = (ShortcutIconResource) extra;
6177 final PackageManager packageManager = context.getPackageManager();
6178 Resources resources = packageManager.getResourcesForApplication(
6179 iconResource.packageName);
6180 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
6181 icon = Utilities.createIconBitmap(
6182 mIconCache.getFullResIcon(resources, id),
6183 context);
6184 } catch (Exception e) {
6185 Log.w(TAG, "Could not load shortcut icon: " + extra);
6186 }
6187 }
6188 }
6189
6190 final ShortcutInfo info = new ShortcutInfo();
6191
6192 // Only support intents for current user for now. Intents sent from other
6193 // users wouldn't get here without intent forwarding anyway.
6194 info.user = UserHandleCompat.myUserHandle();
6195 if (icon == null) {
6196 if (fallbackIcon != null) {
6197 icon = fallbackIcon;
6198 } else {
6199 icon = mIconCache.getDefaultIcon(info.user);
6200 info.usingFallbackIcon = true;
6201 }
6202 }
6203 info.setIcon(icon);
6204
6205 info.title = name;
6206 info.contentDescription = mUserManager.getBadgedLabelForUser(
6207 info.title.toString(), info.user);
6208 info.intent = intent;
6209 info.customIcon = customIcon;
6210 info.iconResource = iconResource;
6211
6212 return info;
6213 }
6214
6215 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
6216 int iconIndex) {
6217 // If apps can't be on SD, don't even bother.
6218 if (!mAppsCanBeOnRemoveableStorage) {
6219 return false;
6220 }
6221 // If this icon doesn't have a custom icon, check to see
6222 // what's stored in the DB, and if it doesn't match what
6223 // we're going to show, store what we are going to show back
6224 // into the DB. We do this so when we're loading, if the
6225 // package manager can't find an icon (for example because
6226 // the app is on SD) then we can use that instead.
6227 if (!info.customIcon && !info.usingFallbackIcon) {
6228 cache.put(info, c.getBlob(iconIndex));
6229 return true;
6230 }
6231 return false;
6232 }
6233 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
6234 boolean needSave = false;
6235 try {
6236 if (data != null) {
6237 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
6238 Bitmap loaded = info.getIcon(mIconCache);
6239 needSave = !saved.sameAs(loaded);
6240 } else {
6241 needSave = true;
6242 }
6243 } catch (Exception e) {
6244 needSave = true;
6245 }
6246 if (needSave) {
6247 Log.d(TAG, "going to save icon bitmap for info=" + info);
6248 // This is slower than is ideal, but this only happens once
6249 // or when the app is updated with a new icon.
6250 updateItemInDatabase(context, info);
6251 }
6252 }
6253
6254 /**
6255 * Return an existing FolderInfo object if we have encountered this ID previously,
6256 * or make a new one.
6257 */
6258 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
6259 // See if a placeholder was created for us already
6260 FolderInfo folderInfo = folders.get(id);
6261 if (folderInfo == null) {
6262 // No placeholder -- create a new instance
6263 folderInfo = new FolderInfo();
6264 folders.put(id, folderInfo);
6265 }
6266 return folderInfo;
6267 }
6268
6269 public static final Comparator<AppInfo> getAppNameComparator() {
6270 final Collator collator = Collator.getInstance();
6271 return new Comparator<AppInfo>() {
6272 public final int compare(AppInfo a, AppInfo b) {
6273 if (a.user.equals(b.user)) {
6274 int result = collator.compare(a.title.toString().trim(),
6275 b.title.toString().trim());
6276 if (result == 0) {
6277 result = a.componentName.compareTo(b.componentName);
6278 }
6279 return result;
6280 } else {
6281 // TODO Need to figure out rules for sorting
6282 // profiles, this puts work second.
6283 return a.user.toString().compareTo(b.user.toString());
6284 }
6285 }
6286 };
6287 }
6288 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
6289 = new Comparator<AppInfo>() {
6290 public final int compare(AppInfo a, AppInfo b) {
6291 if (a.firstInstallTime < b.firstInstallTime) return 1;
6292 if (a.firstInstallTime > b.firstInstallTime) return -1;
6293 return 0;
6294 }
6295 };
6296 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
6297 if (info.activityInfo != null) {
6298 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
6299 } else {
6300 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
6301 }
6302 }
6303 public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
6304 private Collator mCollator;
6305 private HashMap<Object, CharSequence> mLabelCache;
6306 ShortcutNameComparator(PackageManager pm) {
6307 mLabelCache = new HashMap<Object, CharSequence>();
6308 mCollator = Collator.getInstance();
6309 }
6310 ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
6311 mLabelCache = labelCache;
6312 mCollator = Collator.getInstance();
6313 }
6314 public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
6315 String labelA, labelB;
6316 ComponentName keyA = a.getComponentName();
6317 ComponentName keyB = b.getComponentName();
6318 if (mLabelCache.containsKey(keyA)) {
6319 labelA = mLabelCache.get(keyA).toString();
6320 } else {
6321 labelA = a.getLabel().toString().trim();
6322
6323 mLabelCache.put(keyA, labelA);
6324 }
6325 if (mLabelCache.containsKey(keyB)) {
6326 labelB = mLabelCache.get(keyB).toString();
6327 } else {
6328 labelB = b.getLabel().toString().trim();
6329
6330 mLabelCache.put(keyB, labelB);
6331 }
6332 return mCollator.compare(labelA, labelB);
6333 }
6334 };
6335 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
6336 private final AppWidgetManagerCompat mManager;
6337 private final PackageManager mPackageManager;
6338 private final HashMap<Object, String> mLabelCache;
6339 private final Collator mCollator;
6340
6341 WidgetAndShortcutNameComparator(Context context) {
6342 mManager = AppWidgetManagerCompat.getInstance(context);
6343 mPackageManager = context.getPackageManager();
6344 mLabelCache = new HashMap<Object, String>();
6345 mCollator = Collator.getInstance();
6346 }
6347 public final int compare(Object a, Object b) {
6348 String labelA, labelB;
6349 if (mLabelCache.containsKey(a)) {
6350 labelA = mLabelCache.get(a);
6351 } else {
6352 labelA = (a instanceof AppWidgetProviderInfo)
6353 ? mManager.loadLabel((AppWidgetProviderInfo) a)
6354 : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
6355 mLabelCache.put(a, labelA);
6356 }
6357 if (mLabelCache.containsKey(b)) {
6358 labelB = mLabelCache.get(b);
6359 } else {
6360 labelB = (b instanceof AppWidgetProviderInfo)
6361 ? mManager.loadLabel((AppWidgetProviderInfo) b)
6362 : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
6363 mLabelCache.put(b, labelB);
6364 }
6365 return mCollator.compare(labelA, labelB);
6366 }
6367 };
6368
6369 static boolean isValidProvider(AppWidgetProviderInfo provider) {
6370 return (provider != null) && (provider.provider != null)
6371 && (provider.provider.getPackageName() != null);
6372 }
6373
6374 public void dumpState() {
6375 Log.d(TAG, "mCallbacks=" + mCallbacks);
6376 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
6377 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
6378 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
6379 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
6380 if (mLoaderTask != null) {
6381 mLoaderTask.dumpState();
6382 } else {
6383 Log.d(TAG, "mLoaderTask=null");
6384 }
6385 }
6386 } |
1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.launcher3;
18
19 import android.app.SearchManager;
20 import android.appwidget.AppWidgetManager;
21 import android.appwidget.AppWidgetProviderInfo;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.ContentProviderClient;
25 import android.content.ContentProviderOperation;
26 import android.content.ContentResolver;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.Intent.ShortcutIconResource;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ProviderInfo;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Configuration;
37 import android.content.res.Resources;
38 import android.database.Cursor;
39 import android.graphics.Bitmap;
40 import android.graphics.BitmapFactory;
41 import android.net.Uri;
42 import android.os.Environment;
43 import android.os.Handler;
44 import android.os.HandlerThread;
45 import android.os.Parcelable;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.SystemClock;
49 import android.provider.BaseColumns;
50 import android.text.TextUtils;
51 import android.util.Log;
52 import android.util.Pair;
53
54 import com.android.launcher3.compat.AppWidgetManagerCompat;
55 import com.android.launcher3.compat.LauncherActivityInfoCompat;
56 import com.android.launcher3.compat.LauncherAppsCompat;
57 import com.android.launcher3.compat.PackageInstallerCompat;
58 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
59 import com.android.launcher3.compat.UserHandleCompat;
60 import com.android.launcher3.compat.UserManagerCompat;
61
62 import java.lang.ref.WeakReference;
63 import java.net.URISyntaxException;
64 import java.security.InvalidParameterException;
65 import java.text.Collator;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.Collection;
69 import java.util.Collections;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Iterator;
74 import java.util.List;
75 import java.util.Map.Entry;
76 import java.util.Set;
77 import java.util.TreeMap;
78 import java.util.concurrent.atomic.AtomicBoolean;
79
80 /**
81 * Maintains in-memory state of the Launcher. It is expected that there should be only one
82 * LauncherModel object held in a static. Also provide APIs for updating the database state
83 * for the Launcher.
84 */
85 public class LauncherModel extends BroadcastReceiver
86 implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
87 static final boolean DEBUG_LOADERS = false;
88 private static final boolean DEBUG_RECEIVER = false;
89 private static final boolean REMOVE_UNRESTORED_ICONS = true;
90
91 static final String TAG = "Launcher.Model";
92
93 // true = use a "More Apps" folder for non-workspace apps on upgrade
94 // false = strew non-workspace apps across the workspace on upgrade
95 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
96 public static final int LOADER_FLAG_NONE = 0;
97 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
98 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
99
100 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
101 private static final long INVALID_SCREEN_ID = -1L;
102
103 private final boolean mAppsCanBeOnRemoveableStorage;
104 private final boolean mOldContentProviderExists;
105
106 private final LauncherAppState mApp;
107 private final Object mLock = new Object();
108 private DeferredHandler mHandler = new DeferredHandler();
109 private LoaderTask mLoaderTask;
110 private boolean mIsLoaderTaskRunning;
111 private volatile boolean mFlushingWorkerThread;
112
113 // Specific runnable types that are run on the main thread deferred handler, this allows us to
114 // clear all queued binding runnables when the Launcher activity is destroyed.
115 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
116 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
117
118 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
119
120 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
121 static {
122 sWorkerThread.start();
123 }
124 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
125
126 // We start off with everything not loaded. After that, we assume that
127 // our monitoring of the package manager provides all updates and we never
128 // need to do a requery. These are only ever touched from the loader thread.
129 private boolean mWorkspaceLoaded;
130 private boolean mAllAppsLoaded;
131
132 // When we are loading pages synchronously, we can't just post the binding of items on the side
133 // pages as this delays the rotation process. Instead, we wait for a callback from the first
134 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
135 // a normal load, we also clear this set of Runnables.
136 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
137
138 private WeakReference<Callbacks> mCallbacks;
139
140 // < only access in worker thread >
141 AllAppsList mBgAllAppsList;
142
143 // The lock that must be acquired before referencing any static bg data structures. Unlike
144 // other locks, this one can generally be held long-term because we never expect any of these
145 // static data structures to be referenced outside of the worker thread except on the first
146 // load after configuration change.
147 static final Object sBgLock = new Object();
148
149 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
150 // LauncherModel to their ids
151 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
152
153 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
154 // created by LauncherModel that are directly on the home screen (however, no widgets or
155 // shortcuts within folders).
156 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
157
158 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
159 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
160 new ArrayList<LauncherAppWidgetInfo>();
161
162 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
163 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
164
165 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
166 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
167
168 // sBgWorkspaceScreens is the ordered set of workspace screens.
169 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
170
171 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
172 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
173 new HashMap<UserHandleCompat, HashSet<String>>();
174
175 // </ only access in worker thread >
176
177 private IconCache mIconCache;
178
179 protected int mPreviousConfigMcc;
180
181 private final LauncherAppsCompat mLauncherApps;
182 private final UserManagerCompat mUserManager;
183
184 public interface Callbacks {
185 public boolean setLoadOnResume();
186 public int getCurrentWorkspaceScreen();
187 public void startBinding();
188 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
189 boolean forceAnimateIcons);
190 public void bindScreens(ArrayList<Long> orderedScreenIds);
191 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
192 public void bindFolders(HashMap<Long,FolderInfo> folders);
193 public void finishBindingItems(boolean upgradePath);
194 public void bindAppWidget(LauncherAppWidgetInfo info);
195 public void bindAllApplications(ArrayList<AppInfo> apps);
196 public void bindAppsAdded(ArrayList<Long> newScreens,
197 ArrayList<ItemInfo> addNotAnimated,
198 ArrayList<ItemInfo> addAnimated,
199 ArrayList<AppInfo> addedApps);
200 public void bindAppsUpdated(ArrayList<AppInfo> apps);
201 public void bindAppsRestored(ArrayList<AppInfo> apps);
202 public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
203 public void updatePackageBadge(String packageName);
204 public void bindComponentsRemoved(ArrayList<String> packageNames,
205 ArrayList<AppInfo> appInfos, UserHandleCompat user);
206 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
207 public void bindSearchablesChanged();
208 public boolean isAllAppsButtonRank(int rank);
209 public void onPageBoundSynchronously(int page);
210 public void dumpLogsToLocalData();
211 }
212
213 public interface ItemInfoFilter {
214 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
215 }
216
217 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
218 Context context = app.getContext();
219
220 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
221 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
222 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
223 // resource string.
224 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
225 ProviderInfo providerInfo =
226 context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
227 ProviderInfo redirectProvider =
228 context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
229
230 Log.d(TAG, "Old launcher provider: " + oldProvider);
231 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
232
233 if (mOldContentProviderExists) {
234 Log.d(TAG, "Old launcher provider exists.");
235 } else {
236 Log.d(TAG, "Old launcher provider does not exist.");
237 }
238
239 mApp = app;
240 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
241 mIconCache = iconCache;
242
243 final Resources res = context.getResources();
244 Configuration config = res.getConfiguration();
245 mPreviousConfigMcc = config.mcc;
246 mLauncherApps = LauncherAppsCompat.getInstance(context);
247 mUserManager = UserManagerCompat.getInstance(context);
248 }
249
250 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
251 * posted on the main thread handler. */
252 private void runOnMainThread(Runnable r) {
253 runOnMainThread(r, 0);
254 }
255 private void runOnMainThread(Runnable r, int type) {
256 if (sWorkerThread.getThreadId() == Process.myTid()) {
257 // If we are on the worker thread, post onto the main handler
258 mHandler.post(r);
259 } else {
260 r.run();
261 }
262 }
263
264 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
265 * posted on the worker thread handler. */
266 private static void runOnWorkerThread(Runnable r) {
267 if (sWorkerThread.getThreadId() == Process.myTid()) {
268 r.run();
269 } else {
270 // If we are not on the worker thread, then post to the worker handler
271 sWorker.post(r);
272 }
273 }
274
275 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
276 return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
277 }
278
279 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
280 long screen) {
281 LauncherAppState app = LauncherAppState.getInstance();
282 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
283 final int xCount = (int) grid.numColumns;
284 final int yCount = (int) grid.numRows;
285 boolean[][] occupied = new boolean[xCount][yCount];
286
287 int cellX, cellY, spanX, spanY;
288 for (int i = 0; i < items.size(); ++i) {
289 final ItemInfo item = items.get(i);
290 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
291 if (item.screenId == screen) {
292 cellX = item.cellX;
293 cellY = item.cellY;
294 spanX = item.spanX;
295 spanY = item.spanY;
296 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
297 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
298 occupied[x][y] = true;
299 }
300 }
301 }
302 }
303 }
304
305 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
306 }
307 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
308 Intent launchIntent,
309 int firstScreenIndex,
310 ArrayList<Long> workspaceScreens) {
311 // Lock on the app so that we don't try and get the items while apps are being added
312 LauncherAppState app = LauncherAppState.getInstance();
313 LauncherModel model = app.getModel();
314 boolean found = false;
315 synchronized (app) {
316 if (sWorkerThread.getThreadId() != Process.myTid()) {
317 // Flush the LauncherModel worker thread, so that if we just did another
318 // processInstallShortcut, we give it time for its shortcut to get added to the
319 // database (getItemsInLocalCoordinates reads the database)
320 model.flushWorkerThread();
321 }
322 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
323
324 // Try adding to the workspace screens incrementally, starting at the default or center
325 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
326 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
327 int count = workspaceScreens.size();
328 for (int screen = firstScreenIndex; screen < count && !found; screen++) {
329 int[] tmpCoordinates = new int[2];
330 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
331 workspaceScreens.get(screen))) {
332 // Update the Launcher db
333 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
334 }
335 }
336 }
337 return null;
338 }
339
340 public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
341 // Process the updated package state
342 Runnable r = new Runnable() {
343 public void run() {
344 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
345 if (callbacks != null) {
346 callbacks.updatePackageState(installInfo);
347 }
348 }
349 };
350 mHandler.post(r);
351 }
352
353 public void updatePackageBadge(final String packageName) {
354 // Process the updated package badge
355 Runnable r = new Runnable() {
356 public void run() {
357 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
358 if (callbacks != null) {
359 callbacks.updatePackageBadge(packageName);
360 }
361 }
362 };
363 mHandler.post(r);
364 }
365
366 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
367 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
368
369 if (allAppsApps == null) {
370 throw new RuntimeException("allAppsApps must not be null");
371 }
372 if (allAppsApps.isEmpty()) {
373 return;
374 }
375
376 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
377 Iterator<AppInfo> iter = allAppsApps.iterator();
378 while (iter.hasNext()) {
379 ItemInfo a = iter.next();
380 if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
381 restoredAppsFinal.add((AppInfo) a);
382 }
383 }
384
385 // Process the newly added applications and add them to the database first
386 Runnable r = new Runnable() {
387 public void run() {
388 runOnMainThread(new Runnable() {
389 public void run() {
390 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
391 if (callbacks == cb && cb != null) {
392 if (!restoredAppsFinal.isEmpty()) {
393 for (AppInfo info : restoredAppsFinal) {
394 final Intent intent = info.getIntent();
395 if (intent != null) {
396 mIconCache.deletePreloadedIcon(intent.getComponent(),
397 info.user);
398 }
399 }
400 callbacks.bindAppsUpdated(restoredAppsFinal);
401 }
402 callbacks.bindAppsAdded(null, null, null, allAppsApps);
403 }
404 }
405 });
406 }
407 };
408 runOnWorkerThread(r);
409 }
410
411 public void addAndBindAddedWorkspaceApps(final Context context,
412 final ArrayList<ItemInfo> workspaceApps) {
413 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
414
415 if (workspaceApps == null) {
416 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
417 }
418 if (workspaceApps.isEmpty()) {
419 return;
420 }
421 // Process the newly added applications and add them to the database first
422 Runnable r = new Runnable() {
423 public void run() {
424 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
425 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
426 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
427
428 // Get the list of workspace screens. We need to append to this list and
429 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
430 // called.
431 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
432 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
433 for (Integer i : orderedScreens.keySet()) {
434 long screenId = orderedScreens.get(i);
435 workspaceScreens.add(screenId);
436 }
437
438 synchronized(sBgLock) {
439 Iterator<ItemInfo> iter = workspaceApps.iterator();
440 while (iter.hasNext()) {
441 ItemInfo a = iter.next();
442 final String name = a.title.toString();
443 final Intent launchIntent = a.getIntent();
444
445 // Short-circuit this logic if the icon exists somewhere on the workspace
446 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
447 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
448 if (a instanceof AppInfo &&
449 LauncherModel.appWasPromise(context, launchIntent, a.user)) {
450 restoredAppsFinal.add((AppInfo) a);
451 }
452 continue;
453 }
454
455 // Add this icon to the db, creating a new page if necessary. If there
456 // is only the empty page then we just add items to the first page.
457 // Otherwise, we add them to the next pages.
458 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
459 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
460 name, launchIntent, startSearchPageIndex, workspaceScreens);
461 if (coords == null) {
462 LauncherProvider lp = LauncherAppState.getLauncherProvider();
463
464 // If we can't find a valid position, then just add a new screen.
465 // This takes time so we need to re-queue the add until the new
466 // page is added. Create as many screens as necessary to satisfy
467 // the startSearchPageIndex.
468 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
469 workspaceScreens.size());
470 while (numPagesToAdd > 0) {
471 long screenId = lp.generateNewScreenId();
472 // Save the screen id for binding in the workspace
473 workspaceScreens.add(screenId);
474 addedWorkspaceScreensFinal.add(screenId);
475 numPagesToAdd--;
476 }
477
478 // Find the coordinate again
479 coords = LauncherModel.findNextAvailableIconSpace(context,
480 name, launchIntent, startSearchPageIndex, workspaceScreens);
481 }
482 if (coords == null) {
483 throw new RuntimeException("Coordinates should not be null");
484 }
485
486 ShortcutInfo shortcutInfo;
487 if (a instanceof ShortcutInfo) {
488 shortcutInfo = (ShortcutInfo) a;
489 } else if (a instanceof AppInfo) {
490 shortcutInfo = ((AppInfo) a).makeShortcut();
491 } else {
492 throw new RuntimeException("Unexpected info type");
493 }
494
495 // Add the shortcut to the db
496 addItemToDatabase(context, shortcutInfo,
497 LauncherSettings.Favorites.CONTAINER_DESKTOP,
498 coords.first, coords.second[0], coords.second[1], false);
499 // Save the ShortcutInfo for binding in the workspace
500 addedShortcutsFinal.add(shortcutInfo);
501 }
502 }
503
504 // Update the workspace screens
505 updateWorkspaceScreenOrder(context, workspaceScreens);
506
507 if (!addedShortcutsFinal.isEmpty()) {
508 runOnMainThread(new Runnable() {
509 public void run() {
510 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
511 if (callbacks == cb && cb != null) {
512 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
513 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
514 if (!addedShortcutsFinal.isEmpty()) {
515 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 🔵
516 long lastScreenId = info.screenId;
517 for (ItemInfo i : addedShortcutsFinal) {
518 if (i.screenId == lastScreenId) {
519 addAnimated.add(i);
520 } else {
521 addNotAnimated.add(i);
522 }
523 }
524 }
525 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
526 addNotAnimated, addAnimated, null);
527 if (!restoredAppsFinal.isEmpty()) {
528 callbacks.bindAppsUpdated(restoredAppsFinal);
529 }
530 }
531 }
532 });
533 }
534 }
535 };
536 runOnWorkerThread(r);
537 }
538
539 public void unbindItemInfosAndClearQueuedBindRunnables() {
540 if (sWorkerThread.getThreadId() == Process.myTid()) {
541 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
542 "main thread");
543 }
544
545 // Clear any deferred bind runnables
546 synchronized (mDeferredBindRunnables) {
547 mDeferredBindRunnables.clear();
548 }
549 // Remove any queued bind runnables
550 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
551 // Unbind all the workspace items
552 unbindWorkspaceItemsOnMainThread();
553 }
554
555 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
556 void unbindWorkspaceItemsOnMainThread() {
557 // Ensure that we don't use the same workspace items data structure on the main thread
558 // by making a copy of workspace items first.
559 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
560 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
561 synchronized (sBgLock) {
562 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
563 tmpAppWidgets.addAll(sBgAppWidgets);
564 }
565 Runnable r = new Runnable() {
566 @Override
567 public void run() {
568 for (ItemInfo item : tmpWorkspaceItems) {
569 item.unbind();
570 }
571 for (ItemInfo item : tmpAppWidgets) {
572 item.unbind();
573 }
574 }
575 };
576 runOnMainThread(r);
577 }
578
579 /**
580 * Adds an item to the DB if it was not created previously, or move it to a new
581 * <container, screen, cellX, cellY>
582 */
583 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
584 long screenId, int cellX, int cellY) {
585 if (item.container == ItemInfo.NO_ID) {
586 // From all apps
587 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
588 } else {
589 // From somewhere else
590 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
591 }
592 }
593
594 static void checkItemInfoLocked(
595 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
596 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
597 if (modelItem != null && item != modelItem) {
598 // check all the data is consistent
599 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
600 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
601 ShortcutInfo shortcut = (ShortcutInfo) item;
602 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
603 modelShortcut.intent.filterEquals(shortcut.intent) &&
604 modelShortcut.id == shortcut.id &&
605 modelShortcut.itemType == shortcut.itemType &&
606 modelShortcut.container == shortcut.container &&
607 modelShortcut.screenId == shortcut.screenId &&
608 modelShortcut.cellX == shortcut.cellX &&
609 modelShortcut.cellY == shortcut.cellY &&
610 modelShortcut.spanX == shortcut.spanX &&
611 modelShortcut.spanY == shortcut.spanY &&
612 ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
613 (modelShortcut.dropPos != null &&
614 shortcut.dropPos != null &&
615 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
616 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
617 // For all intents and purposes, this is the same object
618 return;
619 }
620 }
621
622 // the modelItem needs to match up perfectly with item if our model is
623 // to be consistent with the database-- for now, just require
624 // modelItem == item or the equality check above
625 String msg = "item: " + ((item != null) ? item.toString() : "null") +
626 "modelItem: " +
627 ((modelItem != null) ? modelItem.toString() : "null") +
628 "Error: ItemInfo passed to checkItemInfo doesn't match original";
629 RuntimeException e = new RuntimeException(msg);
630 if (stackTrace != null) {
631 e.setStackTrace(stackTrace);
632 }
633 throw e;
634 }
635 }
636
637 static void checkItemInfo(final ItemInfo item) {
638 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
639 final long itemId = item.id;
640 Runnable r = new Runnable() {
641 public void run() {
642 synchronized (sBgLock) {
643 checkItemInfoLocked(itemId, item, stackTrace);
644 }
645 }
646 };
647 runOnWorkerThread(r);
648 }
649
650 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
651 final ItemInfo item, final String callingFunction) {
652 final long itemId = item.id;
653 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
654 final ContentResolver cr = context.getContentResolver();
655
656 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
657 Runnable r = new Runnable() {
658 public void run() {
659 cr.update(uri, values, null, null);
660 updateItemArrays(item, itemId, stackTrace);
661 }
662 };
663 runOnWorkerThread(r);
664 }
665
666 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
667 final ArrayList<ItemInfo> items, final String callingFunction) {
668 final ContentResolver cr = context.getContentResolver();
669
670 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
671 Runnable r = new Runnable() {
672 public void run() {
673 ArrayList<ContentProviderOperation> ops =
674 new ArrayList<ContentProviderOperation>();
675 int count = items.size();
676 for (int i = 0; i < count; i++) {
677 ItemInfo item = items.get(i);
678 final long itemId = item.id;
679 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
680 ContentValues values = valuesList.get(i);
681
682 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
683 updateItemArrays(item, itemId, stackTrace);
684
685 }
686 try {
687 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
688 } catch (Exception e) {
689 e.printStackTrace();
690 }
691 }
692 };
693 runOnWorkerThread(r);
694 }
695
696 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
697 // Lock on mBgLock *after* the db operation
698 synchronized (sBgLock) {
699 checkItemInfoLocked(itemId, item, stackTrace);
700
701 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
702 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
703 // Item is in a folder, make sure this folder exists
704 if (!sBgFolders.containsKey(item.container)) {
705 // An items container is being set to a that of an item which is not in
706 // the list of Folders.
707 String msg = "item: " + item + " container being set to: " +
708 item.container + ", not in the list of folders";
709 Log.e(TAG, msg);
710 }
711 }
712
713 // Items are added/removed from the corresponding FolderInfo elsewhere, such
714 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
715 // that are on the desktop, as appropriate
716 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
717 if (modelItem != null &&
718 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
719 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
720 switch (modelItem.itemType) {
721 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
722 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
723 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
724 if (!sBgWorkspaceItems.contains(modelItem)) {
725 sBgWorkspaceItems.add(modelItem);
726 }
727 break;
728 default:
729 break;
730 }
731 } else {
732 sBgWorkspaceItems.remove(modelItem);
733 }
734 }
735 }
736
737 public void flushWorkerThread() {
738 mFlushingWorkerThread = true;
739 Runnable waiter = new Runnable() {
740 public void run() {
741 synchronized (this) {
742 notifyAll();
743 mFlushingWorkerThread = false;
744 }
745 }
746 };
747
748 synchronized(waiter) {
749 runOnWorkerThread(waiter);
750 if (mLoaderTask != null) {
751 synchronized(mLoaderTask) {
752 mLoaderTask.notify();
753 }
754 }
755 boolean success = false;
756 while (!success) {
757 try {
758 waiter.wait();
759 success = true;
760 } catch (InterruptedException e) {
761 }
762 }
763 }
764 }
765
766 /**
767 * Move an item in the DB to a new <container, screen, cellX, cellY>
768 */
769 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
770 final long screenId, final int cellX, final int cellY) {
771 item.container = container;
772 item.cellX = cellX;
773 item.cellY = cellY;
774
775 // We store hotseat items in canonical form which is this orientation invariant position
776 // in the hotseat
777 if (context instanceof Launcher && screenId < 0 &&
778 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
779 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
780 } else {
781 item.screenId = screenId;
782 }
783
784 final ContentValues values = new ContentValues();
785 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
786 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
787 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
788 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
789
790 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
791 }
792
793 /**
794 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
795 * cellX, cellY have already been updated on the ItemInfos.
796 */
797 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
798 final long container, final int screen) {
799
800 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
801 int count = items.size();
802
803 for (int i = 0; i < count; i++) {
804 ItemInfo item = items.get(i);
805 item.container = container;
806
807 // We store hotseat items in canonical form which is this orientation invariant position
808 // in the hotseat
809 if (context instanceof Launcher && screen < 0 &&
810 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
811 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
812 item.cellY);
813 } else {
814 item.screenId = screen;
815 }
816
817 final ContentValues values = new ContentValues();
818 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
819 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
820 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
821 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
822
823 contentValues.add(values);
824 }
825 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
826 }
827
828 /**
829 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
830 */
831 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
832 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
833 item.container = container;
834 item.cellX = cellX;
835 item.cellY = cellY;
836 item.spanX = spanX;
837 item.spanY = spanY;
838
839 // We store hotseat items in canonical form which is this orientation invariant position
840 // in the hotseat
841 if (context instanceof Launcher && screenId < 0 &&
842 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
843 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
844 } else {
845 item.screenId = screenId;
846 }
847
848 final ContentValues values = new ContentValues();
849 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
850 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
851 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
852 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
853 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
854 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
855
856 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
857 }
858
859 /**
860 * Update an item to the database in a specified container.
861 */
862 static void updateItemInDatabase(Context context, final ItemInfo item) {
863 final ContentValues values = new ContentValues();
864 item.onAddToDatabase(context, values);
865 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
866 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
867 }
868
869 /**
870 * Returns true if the shortcuts already exists in the database.
871 * we identify a shortcut by its title and intent.
872 */
873 static boolean shortcutExists(Context context, String title, Intent intent) {
874 final ContentResolver cr = context.getContentResolver();
875 final Intent intentWithPkg, intentWithoutPkg;
876
877 if (intent.getComponent() != null) {
878 // If component is not null, an intent with null package will produce
879 // the same result and should also be a match.
880 if (intent.getPackage() != null) {
881 intentWithPkg = intent;
882 intentWithoutPkg = new Intent(intent).setPackage(null);
883 } else {
884 intentWithPkg = new Intent(intent).setPackage(
885 intent.getComponent().getPackageName());
886 intentWithoutPkg = intent;
887 }
888 } else {
889 intentWithPkg = intent;
890 intentWithoutPkg = intent;
891 }
892 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
893 new String[] { "title", "intent" }, "title=? and (intent=? or intent=?)",
894 new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null);
895 boolean result = false;
896 try {
897 result = c.moveToFirst();
898 } finally {
899 c.close();
900 }
901 return result;
902 }
903
904 /**
905 * Returns true if the promise shortcuts with the same package name exists on the workspace.
906 */
907 static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
908 final ComponentName component = intent.getComponent();
909 if (component == null) {
910 return false;
911 }
912 return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
913 }
914
915 /**
916 * Returns an ItemInfo array containing all the items in the LauncherModel.
917 * The ItemInfo.id is not set through this function.
918 */
919 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
920 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
921 final ContentResolver cr = context.getContentResolver();
922 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
923 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
924 LauncherSettings.Favorites.SCREEN,
925 LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
926 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
927 LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
928
929 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
930 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
931 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
932 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
933 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
934 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
935 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
936 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
937 UserManagerCompat userManager = UserManagerCompat.getInstance(context);
938 try {
939 while (c.moveToNext()) {
940 ItemInfo item = new ItemInfo();
941 item.cellX = c.getInt(cellXIndex);
942 item.cellY = c.getInt(cellYIndex);
943 item.spanX = Math.max(1, c.getInt(spanXIndex));
944 item.spanY = Math.max(1, c.getInt(spanYIndex));
945 item.container = c.getInt(containerIndex);
946 item.itemType = c.getInt(itemTypeIndex);
947 item.screenId = c.getInt(screenIndex);
948 long serialNumber = c.getInt(profileIdIndex);
949 item.user = userManager.getUserForSerialNumber(serialNumber);
950 // Skip if user has been deleted.
951 if (item.user != null) {
952 items.add(item);
953 }
954 }
955 } catch (Exception e) {
956 items.clear();
957 } finally {
958 c.close();
959 }
960
961 return items;
962 }
963
964 /**
965 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
966 */
967 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
968 final ContentResolver cr = context.getContentResolver();
969 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
970 "_id=? and (itemType=? or itemType=?)",
971 new String[] { String.valueOf(id),
972 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
973
974 try {
975 if (c.moveToFirst()) {
976 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
977 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
978 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
979 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
980 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
981 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
982
983 FolderInfo folderInfo = null;
984 switch (c.getInt(itemTypeIndex)) {
985 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
986 folderInfo = findOrMakeFolder(folderList, id);
987 break;
988 }
989
990 folderInfo.title = c.getString(titleIndex);
991 folderInfo.id = id;
992 folderInfo.container = c.getInt(containerIndex);
993 folderInfo.screenId = c.getInt(screenIndex);
994 folderInfo.cellX = c.getInt(cellXIndex);
995 folderInfo.cellY = c.getInt(cellYIndex);
996
997 return folderInfo;
998 }
999 } finally {
1000 c.close();
1001 }
1002
1003 return null;
1004 }
1005
1006 /**
1007 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1008 * cellY fields of the item. Also assigns an ID to the item.
1009 */
1010 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
1011 final long screenId, final int cellX, final int cellY, final boolean notify) {
1012 item.container = container;
1013 item.cellX = cellX;
1014 item.cellY = cellY;
1015 // We store hotseat items in canonical form which is this orientation invariant position
1016 // in the hotseat
1017 if (context instanceof Launcher && screenId < 0 &&
1018 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1019 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
1020 } else {
1021 item.screenId = screenId;
1022 }
1023
1024 final ContentValues values = new ContentValues();
1025 final ContentResolver cr = context.getContentResolver();
1026 item.onAddToDatabase(context, values);
1027
1028 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
1029 values.put(LauncherSettings.Favorites._ID, item.id);
1030 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
1031
1032 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1033 Runnable r = new Runnable() {
1034 public void run() {
1035 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1036 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1037
1038 // Lock on mBgLock *after* the db operation
1039 synchronized (sBgLock) {
1040 checkItemInfoLocked(item.id, item, stackTrace);
1041 sBgItemsIdMap.put(item.id, item);
1042 switch (item.itemType) {
1043 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1044 sBgFolders.put(item.id, (FolderInfo) item);
1045 // Fall through
1046 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1047 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1048 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
1049 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1050 sBgWorkspaceItems.add(item);
1051 } else {
1052 if (!sBgFolders.containsKey(item.container)) {
1053 // Adding an item to a folder that doesn't exist.
1054 String msg = "adding item: " + item + " to a folder that " +
1055 " doesn't exist";
1056 Log.e(TAG, msg);
1057 }
1058 }
1059 break;
1060 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1061 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
1062 break;
1063 }
1064 }
1065 }
1066 };
1067 runOnWorkerThread(r);
1068 }
1069
1070 /**
1071 * Creates a new unique child id, for a given cell span across all layouts.
1072 */
1073 static int getCellLayoutChildId(
1074 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
1075 return (((int) container & 0xFF) << 24)
1076 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
1077 }
1078
1079 private static ArrayList<ItemInfo> getItemsByPackageName(
1080 final String pn, final UserHandleCompat user) {
1081 ItemInfoFilter filter = new ItemInfoFilter() {
1082 @Override
1083 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
1084 return cn.getPackageName().equals(pn) && info.user.equals(user);
1085 }
1086 };
1087 return filterItemInfos(sBgItemsIdMap.values(), filter);
1088 }
1089
1090 /**
1091 * Removes all the items from the database corresponding to the specified package.
1092 */
1093 static void deletePackageFromDatabase(Context context, final String pn,
1094 final UserHandleCompat user) {
1095 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
1096 }
1097
1098 /**
1099 * Removes the specified item from the database
1100 * @param context
1101 * @param item
1102 */
1103 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
1104 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1105 items.add(item);
1106 deleteItemsFromDatabase(context, items);
1107 }
1108
1109 /**
1110 * Removes the specified items from the database
1111 * @param context
1112 * @param item
1113 */
1114 static void deleteItemsFromDatabase(Context context, final ArrayList<ItemInfo> items) {
1115 final ContentResolver cr = context.getContentResolver();
1116
1117 Runnable r = new Runnable() {
1118 public void run() {
1119 for (ItemInfo item : items) {
1120 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
1121 cr.delete(uri, null, null);
1122
1123 // Lock on mBgLock *after* the db operation
1124 synchronized (sBgLock) {
1125 switch (item.itemType) {
1126 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1127 sBgFolders.remove(item.id);
1128 for (ItemInfo info: sBgItemsIdMap.values()) {
1129 if (info.container == item.id) {
1130 // We are deleting a folder which still contains items that
1131 // think they are contained by that folder.
1132 String msg = "deleting a folder (" + item + ") which still " +
1133 "contains items (" + info + ")";
1134 Log.e(TAG, msg);
1135 }
1136 }
1137 sBgWorkspaceItems.remove(item);
1138 break;
1139 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1140 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1141 sBgWorkspaceItems.remove(item);
1142 break;
1143 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1144 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
1145 break;
1146 }
1147 sBgItemsIdMap.remove(item.id);
1148 sBgDbIconCache.remove(item);
1149 }
1150 }
1151 }
1152 };
1153 runOnWorkerThread(r);
1154 }
1155
1156 /**
1157 * Update the order of the workspace screens in the database. The array list contains
1158 * a list of screen ids in the order that they should appear.
1159 */
1160 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
1161 // Log to disk
1162 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
1163 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
1164
1165 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
1166 final ContentResolver cr = context.getContentResolver();
1167 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1168
1169 // Remove any negative screen ids -- these aren't persisted
1170 Iterator<Long> iter = screensCopy.iterator();
1171 while (iter.hasNext()) {
1172 long id = iter.next();
1173 if (id < 0) {
1174 iter.remove();
1175 }
1176 }
1177
1178 Runnable r = new Runnable() {
1179 @Override
1180 public void run() {
1181 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1182 // Clear the table
1183 ops.add(ContentProviderOperation.newDelete(uri).build());
1184 int count = screensCopy.size();
1185 for (int i = 0; i < count; i++) {
1186 ContentValues v = new ContentValues();
1187 long screenId = screensCopy.get(i);
1188 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1189 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1190 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
1191 }
1192
1193 try {
1194 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1195 } catch (Exception ex) {
1196 throw new RuntimeException(ex);
1197 }
1198
1199 synchronized (sBgLock) {
1200 sBgWorkspaceScreens.clear();
1201 sBgWorkspaceScreens.addAll(screensCopy);
1202 }
1203 }
1204 };
1205 runOnWorkerThread(r);
1206 }
1207
1208 /**
1209 * Remove the contents of the specified folder from the database
1210 */
1211 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
1212 final ContentResolver cr = context.getContentResolver();
1213
1214 Runnable r = new Runnable() {
1215 public void run() {
1216 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1217 // Lock on mBgLock *after* the db operation
1218 synchronized (sBgLock) {
1219 sBgItemsIdMap.remove(info.id);
1220 sBgFolders.remove(info.id);
1221 sBgDbIconCache.remove(info);
1222 sBgWorkspaceItems.remove(info);
1223 }
1224
1225 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
1226 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1227 // Lock on mBgLock *after* the db operation
1228 synchronized (sBgLock) {
1229 for (ItemInfo childInfo : info.contents) {
1230 sBgItemsIdMap.remove(childInfo.id);
1231 sBgDbIconCache.remove(childInfo);
1232 }
1233 }
1234 }
1235 };
1236 runOnWorkerThread(r);
1237 }
1238
1239 /**
1240 * Set this as the current Launcher activity object for the loader.
1241 */
1242 public void initialize(Callbacks callbacks) {
1243 synchronized (mLock) {
1244 mCallbacks = new WeakReference<Callbacks>(callbacks);
1245 }
1246 }
1247
1248 @Override
1249 public void onPackageChanged(String packageName, UserHandleCompat user) {
1250 int op = PackageUpdatedTask.OP_UPDATE;
1251 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1252 user));
1253 }
1254
1255 @Override
1256 public void onPackageRemoved(String packageName, UserHandleCompat user) {
1257 int op = PackageUpdatedTask.OP_REMOVE;
1258 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1259 user));
1260 }
1261
1262 @Override
1263 public void onPackageAdded(String packageName, UserHandleCompat user) {
1264 int op = PackageUpdatedTask.OP_ADD;
1265 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1266 user));
1267 }
1268
1269 @Override
1270 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
1271 boolean replacing) {
1272 if (!replacing) {
1273 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
1274 user));
1275 if (mAppsCanBeOnRemoveableStorage) {
1276 // Only rebind if we support removable storage. It catches the
1277 // case where
1278 // apps on the external sd card need to be reloaded
1279 startLoaderFromBackground();
1280 }
1281 } else {
1282 // If we are replacing then just update the packages in the list
1283 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
1284 packageNames, user));
1285 }
1286 }
1287
1288 @Override
1289 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
1290 boolean replacing) {
1291 if (!replacing) {
1292 enqueuePackageUpdated(new PackageUpdatedTask(
1293 PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
1294 user));
1295 }
1296
1297 }
1298
1299 /**
1300 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1301 * ACTION_PACKAGE_CHANGED.
1302 */
1303 @Override
1304 public void onReceive(Context context, Intent intent) {
1305 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
1306
1307 final String action = intent.getAction();
1308 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
1309 // If we have changed locale we need to clear out the labels in all apps/workspace.
1310 forceReload();
1311 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1312 // Check if configuration change was an mcc/mnc change which would affect app resources
1313 // and we would need to clear out the labels in all apps/workspace. Same handling as
1314 // above for ACTION_LOCALE_CHANGED
1315 Configuration currentConfig = context.getResources().getConfiguration();
1316 if (mPreviousConfigMcc != currentConfig.mcc) {
1317 Log.d(TAG, "Reload apps on config change. curr_mcc:"
1318 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
1319 forceReload();
1320 }
1321 // Update previousConfig
1322 mPreviousConfigMcc = currentConfig.mcc;
1323 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
1324 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
1325 if (mCallbacks != null) {
1326 Callbacks callbacks = mCallbacks.get();
1327 if (callbacks != null) {
1328 callbacks.bindSearchablesChanged();
1329 }
1330 }
1331 }
1332 }
1333
1334 void forceReload() {
1335 resetLoadedState(true, true);
1336
1337 // Do this here because if the launcher activity is running it will be restarted.
1338 // If it's not running startLoaderFromBackground will merely tell it that it needs
1339 // to reload.
1340 startLoaderFromBackground();
1341 }
1342
1343 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1344 synchronized (mLock) {
1345 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1346 // mWorkspaceLoaded to true later
1347 stopLoaderLocked();
1348 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1349 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1350 }
1351 }
1352
1353 /**
1354 * When the launcher is in the background, it's possible for it to miss paired
1355 * configuration changes. So whenever we trigger the loader from the background
1356 * tell the launcher that it needs to re-run the loader when it comes back instead
1357 * of doing it now.
1358 */
1359 public void startLoaderFromBackground() {
1360 boolean runLoader = false;
1361 if (mCallbacks != null) {
1362 Callbacks callbacks = mCallbacks.get();
1363 if (callbacks != null) {
1364 // Only actually run the loader if they're not paused.
1365 if (!callbacks.setLoadOnResume()) {
1366 runLoader = true;
1367 }
1368 }
1369 }
1370 if (runLoader) {
1371 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
1372 }
1373 }
1374
1375 // If there is already a loader task running, tell it to stop.
1376 // returns true if isLaunching() was true on the old task
1377 private boolean stopLoaderLocked() {
1378 boolean isLaunching = false;
1379 LoaderTask oldTask = mLoaderTask;
1380 if (oldTask != null) {
1381 if (oldTask.isLaunching()) {
1382 isLaunching = true;
1383 }
1384 oldTask.stopLocked();
1385 }
1386 return isLaunching;
1387 }
1388
1389 public boolean isCurrentCallbacks(Callbacks callbacks) {
1390 return (mCallbacks != null && mCallbacks.get() == callbacks);
1391 }
1392
1393 public void startLoader(boolean isLaunching, int synchronousBindPage) {
1394 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1395 }
1396
1397 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
1398 synchronized (mLock) {
1399 if (DEBUG_LOADERS) {
1400 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1401 }
1402
1403 // Clear any deferred bind-runnables from the synchronized load process
1404 // We must do this before any loading/binding is scheduled below.
1405 synchronized (mDeferredBindRunnables) {
1406 mDeferredBindRunnables.clear();
1407 }
1408
1409 // Don't bother to start the thread if we know it's not going to do anything
1410 if (mCallbacks != null && mCallbacks.get() != null) {
1411 // If there is already one running, tell it to stop.
1412 // also, don't downgrade isLaunching if we're already running
1413 isLaunching = isLaunching || stopLoaderLocked();
1414 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
1415 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
1416 && mAllAppsLoaded && mWorkspaceLoaded) {
1417 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1418 } else {
1419 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1420 sWorker.post(mLoaderTask);
1421 }
1422 }
1423 }
1424 }
1425
1426 void bindRemainingSynchronousPages() {
1427 // Post the remaining side pages to be loaded
1428 if (!mDeferredBindRunnables.isEmpty()) {
1429 Runnable[] deferredBindRunnables = null;
1430 synchronized (mDeferredBindRunnables) {
1431 deferredBindRunnables = mDeferredBindRunnables.toArray(
1432 new Runnable[mDeferredBindRunnables.size()]);
1433 mDeferredBindRunnables.clear();
1434 }
1435 for (final Runnable r : deferredBindRunnables) {
1436 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
1437 }
1438 }
1439 }
1440
1441 public void stopLoader() {
1442 synchronized (mLock) {
1443 if (mLoaderTask != null) {
1444 mLoaderTask.stopLocked();
1445 }
1446 }
1447 }
1448
1449 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1450 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1451 final ContentResolver contentResolver = context.getContentResolver();
1452 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1453 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1454 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1455
1456 try {
1457 final int idIndex = sc.getColumnIndexOrThrow(
1458 LauncherSettings.WorkspaceScreens._ID);
1459 final int rankIndex = sc.getColumnIndexOrThrow(
1460 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1461 while (sc.moveToNext()) {
1462 try {
1463 long screenId = sc.getLong(idIndex);
1464 int rank = sc.getInt(rankIndex);
1465 orderedScreens.put(rank, screenId);
1466 } catch (Exception e) {
1467 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e,🔵
1468 }
1469 }
1470 } finally {
1471 sc.close();
1472 }
1473
1474 // Log to disk
1475 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1476 ArrayList<String> orderedScreensPairs= new ArrayList<String>();
1477 for (Integer i : orderedScreens.keySet()) {
1478 orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
1479 }
1480 Launcher.addDumpLog(TAG, "11683562 - screens: " +
1481 TextUtils.join(", ", orderedScreensPairs), true);
1482 return orderedScreens;
1483 }
1484
1485 public boolean isAllAppsLoaded() {
1486 return mAllAppsLoaded;
1487 }
1488
1489 boolean isLoadingWorkspace() {
1490 synchronized (mLock) {
1491 if (mLoaderTask != null) {
1492 return mLoaderTask.isLoadingWorkspace();
1493 }
1494 }
1495 return false;
1496 }
1497
1498 /**
1499 * Runnable for the thread that loads the contents of the launcher:
1500 * - workspace icons
1501 * - widgets
1502 * - all apps icons
1503 */
1504 private class LoaderTask implements Runnable {
1505 private Context mContext;
1506 private boolean mIsLaunching;
1507 private boolean mIsLoadingAndBindingWorkspace;
1508 private boolean mStopped;
1509 private boolean mLoadAndBindStepFinished;
1510 private int mFlags;
1511
1512 private HashMap<Object, CharSequence> mLabelCache;
1513
1514 LoaderTask(Context context, boolean isLaunching, int flags) {
1515 mContext = context;
1516 mIsLaunching = isLaunching;
1517 mLabelCache = new HashMap<Object, CharSequence>();
1518 mFlags = flags;
1519 }
1520
1521 boolean isLaunching() {
1522 return mIsLaunching;
1523 }
1524
1525 boolean isLoadingWorkspace() {
1526 return mIsLoadingAndBindingWorkspace;
1527 }
1528
1529 /** Returns whether this is an upgrade path */
1530 private boolean loadAndBindWorkspace() {
1531 mIsLoadingAndBindingWorkspace = true;
1532
1533 // Load the workspace
1534 if (DEBUG_LOADERS) {
1535 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
1536 }
1537
1538 boolean isUpgradePath = false;
1539 if (!mWorkspaceLoaded) {
1540 isUpgradePath = loadWorkspace();
1541 synchronized (LoaderTask.this) {
1542 if (mStopped) {
1543 return isUpgradePath;
1544 }
1545 mWorkspaceLoaded = true;
1546 }
1547 }
1548
1549 // Bind the workspace
1550 bindWorkspace(-1, isUpgradePath);
1551 return isUpgradePath;
1552 }
1553
1554 private void waitForIdle() {
1555 // Wait until the either we're stopped or the other threads are done.
1556 // This way we don't start loading all apps until the workspace has settled
1557 // down.
1558 synchronized (LoaderTask.this) {
1559 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1560
1561 mHandler.postIdle(new Runnable() {
1562 public void run() {
1563 synchronized (LoaderTask.this) {
1564 mLoadAndBindStepFinished = true;
1565 if (DEBUG_LOADERS) {
1566 Log.d(TAG, "done with previous binding step");
1567 }
1568 LoaderTask.this.notify();
1569 }
1570 }
1571 });
1572
1573 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
1574 try {
1575 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1576 // wait no longer than 1sec at a time
1577 this.wait(1000);
1578 } catch (InterruptedException ex) {
1579 // Ignore
1580 }
1581 }
1582 if (DEBUG_LOADERS) {
1583 Log.d(TAG, "waited "
1584 + (SystemClock.uptimeMillis()-workspaceWaitTime)
1585 + "ms for previous step to finish binding");
1586 }
1587 }
1588 }
1589
1590 void runBindSynchronousPage(int synchronousBindPage) {
1591 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
1592 // Ensure that we have a valid page index to load synchronously
1593 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1594 "valid page index");
1595 }
1596 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1597 // Ensure that we don't try and bind a specified page when the pages have not been
1598 // loaded already (we should load everything asynchronously in that case)
1599 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1600 }
1601 synchronized (mLock) {
1602 if (mIsLoaderTaskRunning) {
1603 // Ensure that we are never running the background loading at this point since
1604 // we also touch the background collections
1605 throw new RuntimeException("Error! Background loading is already running");
1606 }
1607 }
1608
1609 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1610 // data structures, we can't allow any other thread to touch that data, but because
1611 // this call is synchronous, we can get away with not locking).
1612
1613 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
1614 // operations from the previous activity. We need to ensure that all queued operations
1615 // are executed before any synchronous binding work is done.
1616 mHandler.flush();
1617
1618 // Divide the set of loaded items into those that we are binding synchronously, and
1619 // everything else that is to be bound normally (asynchronously).
1620 bindWorkspace(synchronousBindPage, false);
1621 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1622 // arise from that.
1623 onlyBindAllApps();
1624 }
1625
1626 public void run() {
1627 boolean isUpgrade = false;
1628
1629 synchronized (mLock) {
1630 mIsLoaderTaskRunning = true;
1631 }
1632 // Optimize for end-user experience: if the Launcher is up and // running with the
1633 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1634 // workspace first (default).
1635 keep_running: {
1636 // Elevate priority when Home launches for the first time to avoid
1637 // starving at boot time. Staring at a blank home is not cool.
1638 synchronized (mLock) {
1639 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
1640 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
1641 android.os.Process.setThreadPriority(mIsLaunching
1642 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
1643 }
1644 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
1645 isUpgrade = loadAndBindWorkspace();
1646
1647 if (mStopped) {
1648 break keep_running;
1649 }
1650
1651 // Whew! Hard work done. Slow us down, and wait until the UI thread has
1652 // settled down.
1653 synchronized (mLock) {
1654 if (mIsLaunching) {
1655 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
1656 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1657 }
1658 }
1659 waitForIdle();
1660
1661 // second step
1662 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
1663 loadAndBindAllApps();
1664
1665 // Restore the default thread priority after we are done loading items
1666 synchronized (mLock) {
1667 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1668 }
1669 }
1670
1671 // Update the saved icons if necessary
1672 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
1673 synchronized (sBgLock) {
1674 for (Object key : sBgDbIconCache.keySet()) {
1675 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
1676 }
1677 sBgDbIconCache.clear();
1678 }
1679
1680 if (LauncherAppState.isDisableAllApps()) {
1681 // Ensure that all the applications that are in the system are
1682 // represented on the home screen.
1683 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
1684 verifyApplications();
1685 }
1686 }
1687
1688 // Clear out this reference, otherwise we end up holding it until all of the
1689 // callback runnables are done.
1690 mContext = null;
1691
1692 synchronized (mLock) {
1693 // If we are still the last one to be scheduled, remove ourselves.
1694 if (mLoaderTask == this) {
1695 mLoaderTask = null;
1696 }
1697 mIsLoaderTaskRunning = false;
1698 }
1699 }
1700
1701 public void stopLocked() {
1702 synchronized (LoaderTask.this) {
1703 mStopped = true;
1704 this.notify();
1705 }
1706 }
1707
1708 /**
1709 * Gets the callbacks object. If we've been stopped, or if the launcher object
1710 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1711 * object that was around when the deferred message was scheduled, and if there's
1712 * a new Callbacks object around then also return null. This will save us from
1713 * calling onto it with data that will be ignored.
1714 */
1715 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1716 synchronized (mLock) {
1717 if (mStopped) {
1718 return null;
1719 }
1720
1721 if (mCallbacks == null) {
1722 return null;
1723 }
1724
1725 final Callbacks callbacks = mCallbacks.get();
1726 if (callbacks != oldCallbacks) {
1727 return null;
1728 }
1729 if (callbacks == null) {
1730 Log.w(TAG, "no mCallbacks");
1731 return null;
1732 }
1733
1734 return callbacks;
1735 }
1736 }
1737
1738 private void verifyApplications() {
1739 final Context context = mApp.getContext();
1740
1741 // Cross reference all the applications in our apps list with items in the workspace
1742 ArrayList<ItemInfo> tmpInfos;
1743 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
1744 synchronized (sBgLock) {
1745 for (AppInfo app : mBgAllAppsList.data) {
1746 tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
1747 if (tmpInfos.isEmpty()) {
1748 // We are missing an application icon, so add this to the workspace
1749 added.add(app);
1750 // This is a rare event, so lets log it
1751 Log.e(TAG, "Missing Application on load: " + app);
1752 }
1753 }
1754 }
1755 if (!added.isEmpty()) {
1756 addAndBindAddedWorkspaceApps(context, added);
1757 }
1758 }
1759
1760 // check & update map of what's occupied; used to discard overlapping/invalid items
1761 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item,
1762 AtomicBoolean deleteOnInvalidPlacement) {
1763 LauncherAppState app = LauncherAppState.getInstance();
1764 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1765 final int countX = (int) grid.numColumns;
1766 final int countY = (int) grid.numRows;
1767
1768 long containerIndex = item.screenId;
1769 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1770 // Return early if we detect that an item is under the hotseat button
1771 if (mCallbacks == null ||
1772 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
1773 deleteOnInvalidPlacement.set(true);
1774 Log.e(TAG, "Error loading shortcut into hotseat " + item
1775 + " into position (" + item.screenId + ":" + item.cellX + ","
1776 + item.cellY + ") occupied by all apps");
1777 return false;
1778 }
1779
1780 final ItemInfo[][] hotseatItems =
1781 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
1782
1783 if (item.screenId >= grid.numHotseatIcons) {
1784 Log.e(TAG, "Error loading shortcut " + item
1785 + " into hotseat position " + item.screenId
1786 + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
1787 + ")");
1788 return false;
1789 }
1790
1791 if (hotseatItems != null) {
1792 if (hotseatItems[(int) item.screenId][0] != null) {
1793 Log.e(TAG, "Error loading shortcut into hotseat " + item
1794 + " into position (" + item.screenId + ":" + item.cellX + ","
1795 + item.cellY + ") occupied by "
1796 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
1797 [(int) item.screenId][0]);
1798 return false;
1799 } else {
1800 hotseatItems[(int) item.screenId][0] = item;
1801 return true;
1802 }
1803 } else {
1804 final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
1805 items[(int) item.screenId][0] = item;
1806 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
1807 return true;
1808 }
1809 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1810 // Skip further checking if it is not the hotseat or workspace container
1811 return true;
1812 }
1813
1814 if (!occupied.containsKey(item.screenId)) {
1815 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
1816 occupied.put(item.screenId, items);
1817 }
1818
1819 final ItemInfo[][] screens = occupied.get(item.screenId);
1820 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1821 item.cellX < 0 || item.cellY < 0 ||
1822 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
1823 Log.e(TAG, "Error loading shortcut " + item
1824 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1825 + item.cellX + "," + item.cellY
1826 + ") out of screen bounds ( " + countX + "x" + countY + ")");
1827 return false;
1828 }
1829
1830 // Check if any workspace icons overlap with each other
1831 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1832 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
1833 if (screens[x][y] != null) {
1834 Log.e(TAG, "Error loading shortcut " + item
1835 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1836 + x + "," + y
1837 + ") occupied by "
1838 + screens[x][y]);
1839 return false;
1840 }
1841 }
1842 }
1843 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1844 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
1845 screens[x][y] = item;
1846 }
1847 }
1848
1849 return true;
1850 }
1851
1852 /** Clears all the sBg data structures */
1853 private void clearSBgDataStructures() {
1854 synchronized (sBgLock) {
1855 sBgWorkspaceItems.clear();
1856 sBgAppWidgets.clear();
1857 sBgFolders.clear();
1858 sBgItemsIdMap.clear();
1859 sBgDbIconCache.clear();
1860 sBgWorkspaceScreens.clear();
1861 }
1862 }
1863
1864 /** Returns whether this is an upgrade path */
1865 private boolean loadWorkspace() {
1866 // Log to disk
1867 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
1868
1869 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1870
1871 final Context context = mContext;
1872 final ContentResolver contentResolver = context.getContentResolver();
1873 final PackageManager manager = context.getPackageManager();
1874 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
1875 final boolean isSafeMode = manager.isSafeMode();
1876 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
1877 final boolean isSdCardReady = context.registerReceiver(null,
1878 new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
1879
1880 LauncherAppState app = LauncherAppState.getInstance();
1881 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1882 int countX = (int) grid.numColumns;
1883 int countY = (int) grid.numRows;
1884
1885 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
1886 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
1887 LauncherAppState.getLauncherProvider().deleteDatabase();
1888 }
1889
1890 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1891 // append the user's Launcher2 shortcuts
1892 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
1893 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
1894 } else {
1895 // Make sure the default workspace is loaded
1896 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
1897 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
1898 }
1899
1900 // This code path is for our old migration code and should no longer be exercised
1901 boolean loadedOldDb = false;
1902
1903 // Log to disk
1904 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
1905
1906 synchronized (sBgLock) {
1907 clearSBgDataStructures();
1908 final HashSet<String> installingPkgs = PackageInstallerCompat
1909 .getInstance(mContext).updateAndGetActiveSessionCache();
1910
1911 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
1912 final ArrayList<Long> restoredRows = new ArrayList<Long>();
1913 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
1914 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
1915 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
1916
1917 // +1 for the hotseat (it can be larger than the workspace)
1918 // Load workspace in reverse order to ensure that latest items are loaded first (and
1919 // before any earlier duplicates)
1920 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
1921
1922 try {
1923 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1924 final int intentIndex = c.getColumnIndexOrThrow
1925 (LauncherSettings.Favorites.INTENT);
1926 final int titleIndex = c.getColumnIndexOrThrow
1927 (LauncherSettings.Favorites.TITLE);
1928 final int iconTypeIndex = c.getColumnIndexOrThrow(
1929 LauncherSettings.Favorites.ICON_TYPE);
1930 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1931 final int iconPackageIndex = c.getColumnIndexOrThrow(
1932 LauncherSettings.Favorites.ICON_PACKAGE);
1933 final int iconResourceIndex = c.getColumnIndexOrThrow(
1934 LauncherSettings.Favorites.ICON_RESOURCE);
1935 final int containerIndex = c.getColumnIndexOrThrow(
1936 LauncherSettings.Favorites.CONTAINER);
1937 final int itemTypeIndex = c.getColumnIndexOrThrow(
1938 LauncherSettings.Favorites.ITEM_TYPE);
1939 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1940 LauncherSettings.Favorites.APPWIDGET_ID);
1941 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
1942 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
1943 final int screenIndex = c.getColumnIndexOrThrow(
1944 LauncherSettings.Favorites.SCREEN);
1945 final int cellXIndex = c.getColumnIndexOrThrow
1946 (LauncherSettings.Favorites.CELLX);
1947 final int cellYIndex = c.getColumnIndexOrThrow
1948 (LauncherSettings.Favorites.CELLY);
1949 final int spanXIndex = c.getColumnIndexOrThrow
1950 (LauncherSettings.Favorites.SPANX);
1951 final int spanYIndex = c.getColumnIndexOrThrow(
1952 LauncherSettings.Favorites.SPANY);
1953 final int restoredIndex = c.getColumnIndexOrThrow(
1954 LauncherSettings.Favorites.RESTORED);
1955 final int profileIdIndex = c.getColumnIndexOrThrow(
1956 LauncherSettings.Favorites.PROFILE_ID);
1957 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1958 //final int displayModeIndex = c.getColumnIndexOrThrow(
1959 // LauncherSettings.Favorites.DISPLAY_MODE);
1960
1961 ShortcutInfo info;
1962 String intentDescription;
1963 LauncherAppWidgetInfo appWidgetInfo;
1964 int container;
1965 long id;
1966 Intent intent;
1967 UserHandleCompat user;
1968
1969 while (!mStopped && c.moveToNext()) {
1970 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
1971 try {
1972 int itemType = c.getInt(itemTypeIndex);
1973 boolean restored = 0 != c.getInt(restoredIndex);
1974 boolean allowMissingTarget = false;
1975
1976 switch (itemType) {
1977 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1978 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1979 id = c.getLong(idIndex);
1980 intentDescription = c.getString(intentIndex);
1981 long serialNumber = c.getInt(profileIdIndex);
1982 user = mUserManager.getUserForSerialNumber(serialNumber);
1983 int promiseType = c.getInt(restoredIndex);
1984 if (user == null) {
1985 // User has been deleted remove the item.
1986 itemsToRemove.add(id);
1987 continue;
1988 }
1989 try {
1990 intent = Intent.parseUri(intentDescription, 0);
1991 ComponentName cn = intent.getComponent();
1992 if (cn != null && cn.getPackageName() != null) {
1993 boolean validPkg = launcherApps.isPackageEnabledForProfile(
1994 cn.getPackageName(), user);
1995 boolean validComponent = validPkg &&
1996 launcherApps.isActivityEnabledForProfile(cn, user);
1997
1998 if (validComponent) {
1999 if (restored) {
2000 // no special handling necessary for this item
2001 restoredRows.add(id);
2002 restored = false;
2003 }
2004 } else if (validPkg) {
2005 intent = null;
2006 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
2007 // We allow auto install apps to have their intent
2008 // updated after an install.
2009 intent = manager.getLaunchIntentForPackage(
2010 cn.getPackageName());
2011 if (intent != null) {
2012 ContentValues values = new ContentValues();
2013 values.put(LauncherSettings.Favorites.INTENT,
2014 intent.toUri(0));
2015 String where = BaseColumns._ID + "= ?";
2016 String[] args = {Long.toString(id)};
2017 contentResolver.update(contentUri, values, where, arg🔵
2018 }
2019 }
2020
2021 if (intent == null) {
2022 // The app is installed but the component is no
2023 // longer available.
2024 Launcher.addDumpLog(TAG,
2025 "Invalid component removed: " + cn, true);
2026 itemsToRemove.add(id);
2027 continue;
2028 } else {
2029 // no special handling necessary for this item
2030 restoredRows.add(id);
2031 restored = false;
2032 }
2033 } else if (restored) {
2034 // Package is not yet available but might be
2035 // installed later.
2036 Launcher.addDumpLog(TAG,
2037 "package not yet restored: " + cn, true);
2038
2039 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
2040 // Restore has started once.
2041 } else if (installingPkgs.contains(cn.getPackageName())) {
2042 // App restore has started. Update the flag
2043 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
2044 ContentValues values = new ContentValues();
2045 values.put(LauncherSettings.Favorites.RESTORED,
2046 promiseType);
2047 String where = BaseColumns._ID + "= ?";
2048 String[] args = {Long.toString(id)};
2049 contentResolver.update(contentUri, values, where, args);
2050
2051 } else if (REMOVE_UNRESTORED_ICONS) {
2052 Launcher.addDumpLog(TAG,
2053 "Unrestored package removed: " + cn, true);
2054 itemsToRemove.add(id);
2055 continue;
2056 }
2057 } else if (isSdCardReady) {
2058 // Do not wait for external media load anymore.
2059 // Log the invalid package, and remove it
2060 Launcher.addDumpLog(TAG,
2061 "Invalid package removed: " + cn, true);
2062 itemsToRemove.add(id);
2063 continue;
2064 } else {
2065 // SdCard is not ready yet. Package might get available,
2066 // once it is ready.
2067 Launcher.addDumpLog(TAG, "Invalid package: " + cn
2068 + " (check again later)", true);
2069 HashSet<String> pkgs = sPendingPackages.get(user);
2070 if (pkgs == null) {
2071 pkgs = new HashSet<String>();
2072 sPendingPackages.put(user, pkgs);
2073 }
2074 pkgs.add(cn.getPackageName());
2075 allowMissingTarget = true;
2076 // Add the icon on the workspace anyway.
2077 }
2078 } else if (cn == null) {
2079 // For shortcuts with no component, keep them as they are
2080 restoredRows.add(id);
2081 restored = false;
2082 }
2083 } catch (URISyntaxException e) {
2084 Launcher.addDumpLog(TAG,
2085 "Invalid uri: " + intentDescription, true);
2086 continue;
2087 }
2088
2089 if (restored) {
2090 if (user.equals(UserHandleCompat.myUserHandle())) {
2091 Launcher.addDumpLog(TAG,
2092 "constructing info for partially restored package",
2093 true);
2094 info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
2095 intent = getRestoredItemIntent(c, context, intent);
2096 } else {
2097 // Don't restore items for other profiles.
2098 itemsToRemove.add(id);
2099 continue;
2100 }
2101 } else if (itemType ==
2102 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
2103 info = getShortcutInfo(manager, intent, user, context, c,
2104 iconIndex, titleIndex, mLabelCache, allowMissingTarget);
2105 } else {
2106 info = getShortcutInfo(c, context, iconTypeIndex,
2107 iconPackageIndex, iconResourceIndex, iconIndex,
2108 titleIndex);
2109
2110 // App shortcuts that used to be automatically added to Launcher
2111 // didn't always have the correct intent flags set, so do that
2112 // here
2113 if (intent.getAction() != null &&
2114 intent.getCategories() != null &&
2115 intent.getAction().equals(Intent.ACTION_MAIN) &&
2116 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
2117 intent.addFlags(
2118 Intent.FLAG_ACTIVITY_NEW_TASK |
2119 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
2120 }
2121 }
2122
2123 if (info != null) {
2124 info.id = id;
2125 info.intent = intent;
2126 container = c.getInt(containerIndex);
2127 info.container = container;
2128 info.screenId = c.getInt(screenIndex);
2129 info.cellX = c.getInt(cellXIndex);
2130 info.cellY = c.getInt(cellYIndex);
2131 info.spanX = 1;
2132 info.spanY = 1;
2133 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
2134 info.isDisabled = isSafeMode
2135 && !Utilities.isSystemApp(context, intent);
2136
2137 // check & update map of what's occupied
2138 deleteOnInvalidPlacement.set(false);
2139 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) {
2140 if (deleteOnInvalidPlacement.get()) {
2141 itemsToRemove.add(id);
2142 }
2143 break;
2144 }
2145
2146 switch (container) {
2147 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2148 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2149 sBgWorkspaceItems.add(info);
2150 break;
2151 default:
2152 // Item is in a user folder
2153 FolderInfo folderInfo =
2154 findOrMakeFolder(sBgFolders, container);
2155 folderInfo.add(info);
2156 break;
2157 }
2158 sBgItemsIdMap.put(info.id, info);
2159
2160 // now that we've loaded everthing re-save it with the
2161 // icon in case it disappears somehow.
2162 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
2163 } else {
2164 throw new RuntimeException("Unexpected null ShortcutInfo");
2165 }
2166 break;
2167
2168 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2169 id = c.getLong(idIndex);
2170 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
2171
2172 folderInfo.title = c.getString(titleIndex);
2173 folderInfo.id = id;
2174 container = c.getInt(containerIndex);
2175 folderInfo.container = container;
2176 folderInfo.screenId = c.getInt(screenIndex);
2177 folderInfo.cellX = c.getInt(cellXIndex);
2178 folderInfo.cellY = c.getInt(cellYIndex);
2179 folderInfo.spanX = 1;
2180 folderInfo.spanY = 1;
2181
2182 // check & update map of what's occupied
2183 deleteOnInvalidPlacement.set(false);
2184 if (!checkItemPlacement(occupied, folderInfo,
2185 deleteOnInvalidPlacement)) {
2186 if (deleteOnInvalidPlacement.get()) {
2187 itemsToRemove.add(id);
2188 }
2189 break;
2190 }
2191
2192 switch (container) {
2193 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2194 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2195 sBgWorkspaceItems.add(folderInfo);
2196 break;
2197 }
2198
2199 if (restored) {
2200 // no special handling required for restored folders
2201 restoredRows.add(id);
2202 }
2203
2204 sBgItemsIdMap.put(folderInfo.id, folderInfo);
2205 sBgFolders.put(folderInfo.id, folderInfo);
2206 break;
2207
2208 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2209 // Read all Launcher-specific widget details
2210 int appWidgetId = c.getInt(appWidgetIdIndex);
2211 String savedProvider = c.getString(appWidgetProviderIndex);
2212 id = c.getLong(idIndex);
2213 final ComponentName component =
2214 ComponentName.unflattenFromString(savedProvider);
2215
2216 final int restoreStatus = c.getInt(restoredIndex);
2217 final boolean isIdValid = (restoreStatus &
2218 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
2219
2220 final boolean wasProviderReady = (restoreStatus &
2221 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
2222
2223 final AppWidgetProviderInfo provider = isIdValid
2224 ? widgets.getAppWidgetInfo(appWidgetId)
2225 : findAppWidgetProviderInfoWithComponent(context, component);
2226
2227 final boolean isProviderReady = isValidProvider(provider);
2228 if (!isSafeMode && wasProviderReady && !isProviderReady) {
2229 String log = "Deleting widget that isn't installed anymore: "
2230 + "id=" + id + " appWidgetId=" + appWidgetId;
2231 Log.e(TAG, log);
2232 Launcher.addDumpLog(TAG, log, false);
2233 itemsToRemove.add(id);
2234 } else {
2235 if (isProviderReady) {
2236 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2237 provider.provider);
2238 int[] minSpan =
2239 Launcher.getMinSpanForWidget(context, provider);
2240 appWidgetInfo.minSpanX = minSpan[0];
2241 appWidgetInfo.minSpanY = minSpan[1];
2242
2243 int status = restoreStatus;
2244 if (!wasProviderReady) {
2245 // If provider was not previously ready, update the
2246 // status and UI flag.
2247
2248 // Id would be valid only if the widget restore broadcast was🔵
2249 if (isIdValid) {
2250 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
2251 } else {
2252 status &= ~LauncherAppWidgetInfo
2253 .FLAG_PROVIDER_NOT_READY;
2254 }
2255 }
2256 appWidgetInfo.restoreStatus = status;
2257 } else {
2258 Log.v(TAG, "Widget restore pending id=" + id
2259 + " appWidgetId=" + appWidgetId
2260 + " status =" + restoreStatus);
2261 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2262 component);
2263 appWidgetInfo.restoreStatus = restoreStatus;
2264
2265 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) 🔵
2266 // Restore has started once.
2267 } else if (installingPkgs.contains(component.getPackageName())) {
2268 // App restore has started. Update the flag
2269 appWidgetInfo.restoreStatus |=
2270 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
2271 } else if (REMOVE_UNRESTORED_ICONS) {
2272 Launcher.addDumpLog(TAG,
2273 "Unrestored widget removed: " + component, true);
2274 itemsToRemove.add(id);
2275 continue;
2276 }
2277 }
2278
2279 appWidgetInfo.id = id;
2280 appWidgetInfo.screenId = c.getInt(screenIndex);
2281 appWidgetInfo.cellX = c.getInt(cellXIndex);
2282 appWidgetInfo.cellY = c.getInt(cellYIndex);
2283 appWidgetInfo.spanX = c.getInt(spanXIndex);
2284 appWidgetInfo.spanY = c.getInt(spanYIndex);
2285
2286 container = c.getInt(containerIndex);
2287 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2288 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2289 Log.e(TAG, "Widget found where container != " +
2290 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
2291 continue;
2292 }
2293
2294 appWidgetInfo.container = c.getInt(containerIndex);
2295 // check & update map of what's occupied
2296 deleteOnInvalidPlacement.set(false);
2297 if (!checkItemPlacement(occupied, appWidgetInfo,
2298 deleteOnInvalidPlacement)) {
2299 if (deleteOnInvalidPlacement.get()) {
2300 itemsToRemove.add(id);
2301 }
2302 break;
2303 }
2304
2305 String providerName = appWidgetInfo.providerName.flattenToString();
2306 if (!providerName.equals(savedProvider) ||
2307 (appWidgetInfo.restoreStatus != restoreStatus)) {
2308 ContentValues values = new ContentValues();
2309 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
2310 providerName);
2311 values.put(LauncherSettings.Favorites.RESTORED,
2312 appWidgetInfo.restoreStatus);
2313 String where = BaseColumns._ID + "= ?";
2314 String[] args = {Long.toString(id)};
2315 contentResolver.update(contentUri, values, where, args);
2316 }
2317 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2318 sBgAppWidgets.add(appWidgetInfo);
2319 }
2320 break;
2321 }
2322 } catch (Exception e) {
2323 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
2324 }
2325 }
2326 } finally {
2327 if (c != null) {
2328 c.close();
2329 }
2330 }
2331
2332 // Break early if we've stopped loading
2333 if (mStopped) {
2334 clearSBgDataStructures();
2335 return false;
2336 }
2337
2338 if (itemsToRemove.size() > 0) {
2339 ContentProviderClient client = contentResolver.acquireContentProviderClient(
2340 contentUri);
2341 // Remove dead items
2342 for (long id : itemsToRemove) {
2343 if (DEBUG_LOADERS) {
2344 Log.d(TAG, "Removed id = " + id);
2345 }
2346 // Don't notify content observers
2347 try {
2348 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
2349 null, null);
2350 } catch (RemoteException e) {
2351 Log.w(TAG, "Could not remove id = " + id);
2352 }
2353 }
2354 }
2355
2356 if (restoredRows.size() > 0) {
2357 ContentProviderClient updater = contentResolver.acquireContentProviderClient(
2358 contentUri);
2359 // Update restored items that no longer require special handling
2360 try {
2361 StringBuilder selectionBuilder = new StringBuilder();
2362 selectionBuilder.append(LauncherSettings.Favorites._ID);
2363 selectionBuilder.append(" IN (");
2364 selectionBuilder.append(TextUtils.join(", ", restoredRows));
2365 selectionBuilder.append(")");
2366 ContentValues values = new ContentValues();
2367 values.put(LauncherSettings.Favorites.RESTORED, 0);
2368 updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
2369 values, selectionBuilder.toString(), null);
2370 } catch (RemoteException e) {
2371 Log.w(TAG, "Could not update restored rows");
2372 }
2373 }
2374
2375 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
2376 context.registerReceiver(new AppsAvailabilityCheck(),
2377 new IntentFilter(StartupReceiver.SYSTEM_READY),
2378 null, sWorker);
2379 }
2380
2381 if (loadedOldDb) {
2382 long maxScreenId = 0;
2383 // If we're importing we use the old screen order.
2384 for (ItemInfo item: sBgItemsIdMap.values()) {
2385 long screenId = item.screenId;
2386 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2387 !sBgWorkspaceScreens.contains(screenId)) {
2388 sBgWorkspaceScreens.add(screenId);
2389 if (screenId > maxScreenId) {
2390 maxScreenId = screenId;
2391 }
2392 }
2393 }
2394 Collections.sort(sBgWorkspaceScreens);
2395 // Log to disk
2396 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
2397 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2398 TextUtils.join(", ", sBgWorkspaceScreens), true);
2399
2400 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
2401 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2402
2403 // Update the max item id after we load an old db
2404 long maxItemId = 0;
2405 // If we're importing we use the old screen order.
2406 for (ItemInfo item: sBgItemsIdMap.values()) {
2407 maxItemId = Math.max(maxItemId, item.id);
2408 }
2409 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
2410 } else {
2411 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
2412 for (Integer i : orderedScreens.keySet()) {
2413 sBgWorkspaceScreens.add(orderedScreens.get(i));
2414 }
2415 // Log to disk
2416 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2417 TextUtils.join(", ", sBgWorkspaceScreens), true);
2418
2419 // Remove any empty screens
2420 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
2421 for (ItemInfo item: sBgItemsIdMap.values()) {
2422 long screenId = item.screenId;
2423 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2424 unusedScreens.contains(screenId)) {
2425 unusedScreens.remove(screenId);
2426 }
2427 }
2428
2429 // If there are any empty screens remove them, and update.
2430 if (unusedScreens.size() != 0) {
2431 // Log to disk
2432 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
2433 TextUtils.join(", ", unusedScreens), true);
2434
2435 sBgWorkspaceScreens.removeAll(unusedScreens);
2436 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2437 }
2438 }
2439
2440 if (DEBUG_LOADERS) {
2441 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
2442 Log.d(TAG, "workspace layout: ");
2443 int nScreens = occupied.size();
2444 for (int y = 0; y < countY; y++) {
2445 String line = "";
2446
2447 Iterator<Long> iter = occupied.keySet().iterator();
2448 while (iter.hasNext()) {
2449 long screenId = iter.next();
2450 if (screenId > 0) {
2451 line += " | ";
2452 }
2453 for (int x = 0; x < countX; x++) {
2454 ItemInfo[][] screen = occupied.get(screenId);
2455 if (x < screen.length && y < screen[x].length) {
2456 line += (screen[x][y] != null) ? "#" : ".";
2457 } else {
2458 line += "!";
2459 }
2460 }
2461 }
2462 Log.d(TAG, "[ " + line + " ]");
2463 }
2464 }
2465 }
2466 return loadedOldDb;
2467 }
2468
2469 /** Filters the set of items who are directly or indirectly (via another container) on the
2470 * specified screen. */
2471 private void filterCurrentWorkspaceItems(long currentScreenId,
2472 ArrayList<ItemInfo> allWorkspaceItems,
2473 ArrayList<ItemInfo> currentScreenItems,
2474 ArrayList<ItemInfo> otherScreenItems) {
2475 // Purge any null ItemInfos
2476 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2477 while (iter.hasNext()) {
2478 ItemInfo i = iter.next();
2479 if (i == null) {
2480 iter.remove();
2481 }
2482 }
2483
2484 // Order the set of items by their containers first, this allows use to walk through the
2485 // list sequentially, build up a list of containers that are in the specified screen,
2486 // as well as all items in those containers.
2487 Set<Long> itemsOnScreen = new HashSet<Long>();
2488 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2489 @Override
2490 public int compare(ItemInfo lhs, ItemInfo rhs) {
2491 return (int) (lhs.container - rhs.container);
2492 }
2493 });
2494 for (ItemInfo info : allWorkspaceItems) {
2495 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
2496 if (info.screenId == currentScreenId) {
2497 currentScreenItems.add(info);
2498 itemsOnScreen.add(info.id);
2499 } else {
2500 otherScreenItems.add(info);
2501 }
2502 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2503 currentScreenItems.add(info);
2504 itemsOnScreen.add(info.id);
2505 } else {
2506 if (itemsOnScreen.contains(info.container)) {
2507 currentScreenItems.add(info);
2508 itemsOnScreen.add(info.id);
2509 } else {
2510 otherScreenItems.add(info);
2511 }
2512 }
2513 }
2514 }
2515
2516 /** Filters the set of widgets which are on the specified screen. */
2517 private void filterCurrentAppWidgets(long currentScreenId,
2518 ArrayList<LauncherAppWidgetInfo> appWidgets,
2519 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
2520 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
2521
2522 for (LauncherAppWidgetInfo widget : appWidgets) {
2523 if (widget == null) continue;
2524 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2525 widget.screenId == currentScreenId) {
2526 currentScreenWidgets.add(widget);
2527 } else {
2528 otherScreenWidgets.add(widget);
2529 }
2530 }
2531 }
2532
2533 /** Filters the set of folders which are on the specified screen. */
2534 private void filterCurrentFolders(long currentScreenId,
2535 HashMap<Long, ItemInfo> itemsIdMap,
2536 HashMap<Long, FolderInfo> folders,
2537 HashMap<Long, FolderInfo> currentScreenFolders,
2538 HashMap<Long, FolderInfo> otherScreenFolders) {
2539
2540 for (long id : folders.keySet()) {
2541 ItemInfo info = itemsIdMap.get(id);
2542 FolderInfo folder = folders.get(id);
2543 if (info == null || folder == null) continue;
2544 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2545 info.screenId == currentScreenId) {
2546 currentScreenFolders.put(id, folder);
2547 } else {
2548 otherScreenFolders.put(id, folder);
2549 }
2550 }
2551 }
2552
2553 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2554 * right) */
2555 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
2556 final LauncherAppState app = LauncherAppState.getInstance();
2557 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2558 // XXX: review this
2559 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
2560 @Override
2561 public int compare(ItemInfo lhs, ItemInfo rhs) {
2562 int cellCountX = (int) grid.numColumns;
2563 int cellCountY = (int) grid.numRows;
2564 int screenOffset = cellCountX * cellCountY;
2565 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
2566 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
2567 lhs.cellY * cellCountX + lhs.cellX);
2568 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
2569 rhs.cellY * cellCountX + rhs.cellX);
2570 return (int) (lr - rr);
2571 }
2572 });
2573 }
2574
2575 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2576 final ArrayList<Long> orderedScreens) {
2577 final Runnable r = new Runnable() {
2578 @Override
2579 public void run() {
2580 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2581 if (callbacks != null) {
2582 callbacks.bindScreens(orderedScreens);
2583 }
2584 }
2585 };
2586 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2587 }
2588
2589 private void bindWorkspaceItems(final Callbacks oldCallbacks,
2590 final ArrayList<ItemInfo> workspaceItems,
2591 final ArrayList<LauncherAppWidgetInfo> appWidgets,
2592 final HashMap<Long, FolderInfo> folders,
2593 ArrayList<Runnable> deferredBindRunnables) {
2594
2595 final boolean postOnMainThread = (deferredBindRunnables != null);
2596
2597 // Bind the workspace items
2598 int N = workspaceItems.size();
2599 for (int i = 0; i < N; i += ITEMS_CHUNK) {
2600 final int start = i;
2601 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
2602 final Runnable r = new Runnable() {
2603 @Override
2604 public void run() {
2605 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2606 if (callbacks != null) {
2607 callbacks.bindItems(workspaceItems, start, start+chunkSize,
2608 false);
2609 }
2610 }
2611 };
2612 if (postOnMainThread) {
2613 synchronized (deferredBindRunnables) {
2614 deferredBindRunnables.add(r);
2615 }
2616 } else {
2617 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2618 }
2619 }
2620
2621 // Bind the folders
2622 if (!folders.isEmpty()) {
2623 final Runnable r = new Runnable() {
2624 public void run() {
2625 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2626 if (callbacks != null) {
2627 callbacks.bindFolders(folders);
2628 }
2629 }
2630 };
2631 if (postOnMainThread) {
2632 synchronized (deferredBindRunnables) {
2633 deferredBindRunnables.add(r);
2634 }
2635 } else {
2636 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2637 }
2638 }
2639
2640 // Bind the widgets, one at a time
2641 N = appWidgets.size();
2642 for (int i = 0; i < N; i++) {
2643 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2644 final Runnable r = new Runnable() {
2645 public void run() {
2646 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2647 if (callbacks != null) {
2648 callbacks.bindAppWidget(widget);
2649 }
2650 }
2651 };
2652 if (postOnMainThread) {
2653 deferredBindRunnables.add(r);
2654 } else {
2655 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2656 }
2657 }
2658 }
2659
2660 /**
2661 * Binds all loaded data to actual views on the main thread.
2662 */
2663 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
2664 final long t = SystemClock.uptimeMillis();
2665 Runnable r;
2666
2667 // Don't use these two variables in any of the callback runnables.
2668 // Otherwise we hold a reference to them.
2669 final Callbacks oldCallbacks = mCallbacks.get();
2670 if (oldCallbacks == null) {
2671 // This launcher has exited and nobody bothered to tell us. Just bail.
2672 Log.w(TAG, "LoaderTask running with no launcher");
2673 return;
2674 }
2675
2676 // Save a copy of all the bg-thread collections
2677 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
2678 ArrayList<LauncherAppWidgetInfo> appWidgets =
2679 new ArrayList<LauncherAppWidgetInfo>();
2680 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
2681 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
2682 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
2683 synchronized (sBgLock) {
2684 workspaceItems.addAll(sBgWorkspaceItems);
2685 appWidgets.addAll(sBgAppWidgets);
2686 folders.putAll(sBgFolders);
2687 itemsIdMap.putAll(sBgItemsIdMap);
2688 orderedScreenIds.addAll(sBgWorkspaceScreens);
2689 }
2690
2691 final boolean isLoadingSynchronously =
2692 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
2693 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
2694 oldCallbacks.getCurrentWorkspaceScreen();
2695 if (currScreen >= orderedScreenIds.size()) {
2696 // There may be no workspace screens (just hotseat items and an empty page).
2697 currScreen = PagedView.INVALID_RESTORE_PAGE;
2698 }
2699 final int currentScreen = currScreen;
2700 final long currentScreenId = currentScreen < 0
2701 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
2702
2703 // Load all the items that are on the current page first (and in the process, unbind
2704 // all the existing workspace items before we call startBinding() below.
2705 unbindWorkspaceItemsOnMainThread();
2706
2707 // Separate the items that are on the current screen, and all the other remaining items
2708 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
2709 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
2710 ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
2711 new ArrayList<LauncherAppWidgetInfo>();
2712 ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
2713 new ArrayList<LauncherAppWidgetInfo>();
2714 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
2715 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
2716
2717 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
2718 otherWorkspaceItems);
2719 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
2720 otherAppWidgets);
2721 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
2722 otherFolders);
2723 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2724 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2725
2726 // Tell the workspace that we're about to start binding items
2727 r = new Runnable() {
2728 public void run() {
2729 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2730 if (callbacks != null) {
2731 callbacks.startBinding();
2732 }
2733 }
2734 };
2735 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2736
2737 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2738
2739 // Load items on the current page
2740 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
2741 currentFolders, null);
2742 if (isLoadingSynchronously) {
2743 r = new Runnable() {
2744 public void run() {
2745 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2746 if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
2747 callbacks.onPageBoundSynchronously(currentScreen);
2748 }
2749 }
2750 };
2751 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2752 }
2753
2754 // Load all the remaining pages (if we are loading synchronously, we want to defer this
2755 // work until after the first render)
2756 synchronized (mDeferredBindRunnables) {
2757 mDeferredBindRunnables.clear();
2758 }
2759 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
2760 (isLoadingSynchronously ? mDeferredBindRunnables : null));
2761
2762 // Tell the workspace that we're done binding items
2763 r = new Runnable() {
2764 public void run() {
2765 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2766 if (callbacks != null) {
2767 callbacks.finishBindingItems(isUpgradePath);
2768 }
2769
2770 // If we're profiling, ensure this is the last thing in the queue.
2771 if (DEBUG_LOADERS) {
2772 Log.d(TAG, "bound workspace in "
2773 + (SystemClock.uptimeMillis()-t) + "ms");
2774 }
2775
2776 mIsLoadingAndBindingWorkspace = false;
2777 }
2778 };
2779 if (isLoadingSynchronously) {
2780 synchronized (mDeferredBindRunnables) {
2781 mDeferredBindRunnables.add(r);
2782 }
2783 } else {
2784 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2785 }
2786 }
2787
2788 private void loadAndBindAllApps() {
2789 if (DEBUG_LOADERS) {
2790 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2791 }
2792 if (!mAllAppsLoaded) {
2793 loadAllApps();
2794 synchronized (LoaderTask.this) {
2795 if (mStopped) {
2796 return;
2797 }
2798 mAllAppsLoaded = true;
2799 }
2800 } else {
2801 onlyBindAllApps();
2802 }
2803 }
2804
2805 private void onlyBindAllApps() {
2806 final Callbacks oldCallbacks = mCallbacks.get();
2807 if (oldCallbacks == null) {
2808 // This launcher has exited and nobody bothered to tell us. Just bail.
2809 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2810 return;
2811 }
2812
2813 // shallow copy
2814 @SuppressWarnings("unchecked")
2815 final ArrayList<AppInfo> list
2816 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
2817 Runnable r = new Runnable() {
2818 public void run() {
2819 final long t = SystemClock.uptimeMillis();
2820 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2821 if (callbacks != null) {
2822 callbacks.bindAllApplications(list);
2823 }
2824 if (DEBUG_LOADERS) {
2825 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
2826 + (SystemClock.uptimeMillis()-t) + "ms");
2827 }
2828 }
2829 };
2830 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
2831 if (isRunningOnMainThread) {
2832 r.run();
2833 } else {
2834 mHandler.post(r);
2835 }
2836 }
2837
2838 private void loadAllApps() {
2839 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2840
2841 final Callbacks oldCallbacks = mCallbacks.get();
2842 if (oldCallbacks == null) {
2843 // This launcher has exited and nobody bothered to tell us. Just bail.
2844 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
2845 return;
2846 }
2847
2848 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
2849 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2850
2851 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
2852
2853 // Clear the list of apps
2854 mBgAllAppsList.clear();
2855 for (UserHandleCompat user : profiles) {
2856 // Query for the set of apps
2857 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2858 List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
2859 if (DEBUG_LOADERS) {
2860 Log.d(TAG, "getActivityList took "
2861 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
2862 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
2863 }
2864 // Fail if we don't have any apps
2865 if (apps == null || apps.isEmpty()) {
2866 return;
2867 }
2868 // Sort the applications by name
2869 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2870 Collections.sort(apps,
2871 new LauncherModel.ShortcutNameComparator(mLabelCache));
2872 if (DEBUG_LOADERS) {
2873 Log.d(TAG, "sort took "
2874 + (SystemClock.uptimeMillis()-sortTime) + "ms");
2875 }
2876
2877 // Create the ApplicationInfos
2878 for (int i = 0; i < apps.size(); i++) {
2879 LauncherActivityInfoCompat app = apps.get(i);
2880 // This builds the icon bitmaps.
2881 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
2882 }
2883 }
2884 // Huh? Shouldn't this be inside the Runnable below?
2885 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2886 mBgAllAppsList.added = new ArrayList<AppInfo>();
2887
2888 // Post callback on main thread
2889 mHandler.post(new Runnable() {
2890 public void run() {
2891 final long bindTime = SystemClock.uptimeMillis();
2892 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2893 if (callbacks != null) {
2894 callbacks.bindAllApplications(added);
2895 if (DEBUG_LOADERS) {
2896 Log.d(TAG, "bound " + added.size() + " apps in "
2897 + (SystemClock.uptimeMillis() - bindTime) + "ms");
2898 }
2899 } else {
2900 Log.i(TAG, "not binding apps: no Launcher activity");
2901 }
2902 }
2903 });
2904
2905 if (DEBUG_LOADERS) {
2906 Log.d(TAG, "Icons processed in "
2907 + (SystemClock.uptimeMillis() - loadTime) + "ms");
2908 }
2909 }
2910
2911 public void dumpState() {
2912 synchronized (sBgLock) {
2913 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
2914 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
2915 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2916 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2917 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2918 }
2919 }
2920 }
2921
2922 void enqueuePackageUpdated(PackageUpdatedTask task) {
2923 sWorker.post(task);
2924 }
2925
2926 private class AppsAvailabilityCheck extends BroadcastReceiver {
2927
2928 @Override
2929 public void onReceive(Context context, Intent intent) {
2930 synchronized (sBgLock) {
2931 final LauncherAppsCompat launcherApps = LauncherAppsCompat
2932 .getInstance(mApp.getContext());
2933 ArrayList<String> packagesRemoved;
2934 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
2935 UserHandleCompat user = entry.getKey();
2936 packagesRemoved = new ArrayList<String>();
2937 for (String pkg : entry.getValue()) {
2938 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
2939 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
2940 packagesRemoved.add(pkg);
2941 }
2942 }
2943 if (!packagesRemoved.isEmpty()) {
2944 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
2945 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
2946 }
2947 }
2948 sPendingPackages.clear();
2949 }
2950 }
2951 }
2952
2953 /**
2954 * Workaround to re-check unrestored items, in-case they were installed but the Package-ADD
2955 * runnable was missed by the launcher.
2956 */
2957 public void recheckRestoredItems(final Context context) {
2958 Runnable r = new Runnable() {
2959
2960 @Override
2961 public void run() {
2962 LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2963 HashSet<String> installedPackages = new HashSet<String>();
2964 UserHandleCompat user = UserHandleCompat.myUserHandle();
2965 synchronized(sBgLock) {
2966 for (ItemInfo info : sBgItemsIdMap.values()) {
2967 if (info instanceof ShortcutInfo) {
2968 ShortcutInfo si = (ShortcutInfo) info;
2969 if (si.isPromise() && si.getTargetComponent() != null
2970 && launcherApps.isPackageEnabledForProfile(
2971 si.getTargetComponent().getPackageName(), user)) {
2972 installedPackages.add(si.getTargetComponent().getPackageName());
2973 }
2974 } else if (info instanceof LauncherAppWidgetInfo) {
2975 LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) info;
2976 if (widget.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
2977 && launcherApps.isPackageEnabledForProfile(
2978 widget.providerName.getPackageName(), user)) {
2979 installedPackages.add(widget.providerName.getPackageName());
2980 }
2981 }
2982 }
2983 }
2984
2985 if (!installedPackages.isEmpty()) {
2986 final ArrayList<AppInfo> restoredApps = new ArrayList<AppInfo>();
2987 for (String pkg : installedPackages) {
2988 for (LauncherActivityInfoCompat info : launcherApps.getActivityList(pkg, user)) {
2989 restoredApps.add(new AppInfo(context, info, user, mIconCache, null));
2990 }
2991 }
2992
2993 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
2994 if (!restoredApps.isEmpty()) {
2995 mHandler.post(new Runnable() {
2996 public void run() {
2997 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
2998 if (callbacks == cb && cb != null) {
2999 callbacks.bindAppsRestored(restoredApps);
3000 }
3001 }
3002 });
3003 }
3004
3005 }
3006 }
3007 };
3008 sWorker.post(r);
3009 }
3010
3011 private class PackageUpdatedTask implements Runnable {
3012 int mOp;
3013 String[] mPackages;
3014 UserHandleCompat mUser;
3015
3016 public static final int OP_NONE = 0;
3017 public static final int OP_ADD = 1;
3018 public static final int OP_UPDATE = 2;
3019 public static final int OP_REMOVE = 3; // uninstlled
3020 public static final int OP_UNAVAILABLE = 4; // external media unmounted
3021
3022
3023 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
3024 mOp = op;
3025 mPackages = packages;
3026 mUser = user;
3027 }
3028
3029 public void run() {
3030 final Context context = mApp.getContext();
3031
3032 final String[] packages = mPackages;
3033 final int N = packages.length;
3034 switch (mOp) {
3035 case OP_ADD:
3036 for (int i=0; i<N; i++) {
3037 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
3038 mIconCache.remove(packages[i], mUser);
3039 mBgAllAppsList.addPackage(context, packages[i], mUser);
3040 }
3041 break;
3042 case OP_UPDATE:
3043 for (int i=0; i<N; i++) {
3044 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
3045 mBgAllAppsList.updatePackage(context, packages[i], mUser);
3046 WidgetPreviewLoader.removePackageFromDb(
3047 mApp.getWidgetPreviewCacheDb(), packages[i]);
3048 }
3049 break;
3050 case OP_REMOVE:
3051 case OP_UNAVAILABLE:
3052 for (int i=0; i<N; i++) {
3053 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
3054 mBgAllAppsList.removePackage(packages[i], mUser);
3055 WidgetPreviewLoader.removePackageFromDb(
3056 mApp.getWidgetPreviewCacheDb(), packages[i]);
3057 }
3058 break;
3059 }
3060
3061 ArrayList<AppInfo> added = null;
3062 ArrayList<AppInfo> modified = null;
3063 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
3064
3065 if (mBgAllAppsList.added.size() > 0) {
3066 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
3067 mBgAllAppsList.added.clear();
3068 }
3069 if (mBgAllAppsList.modified.size() > 0) {
3070 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
3071 mBgAllAppsList.modified.clear();
3072 }
3073 if (mBgAllAppsList.removed.size() > 0) {
3074 removedApps.addAll(mBgAllAppsList.removed);
3075 mBgAllAppsList.removed.clear();
3076 }
3077
3078 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
3079 if (callbacks == null) {
3080 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
3081 return;
3082 }
3083
3084 if (added != null) {
3085 // Ensure that we add all the workspace applications to the db
3086 if (LauncherAppState.isDisableAllApps()) {
3087 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
3088 addAndBindAddedWorkspaceApps(context, addedInfos);
3089 } else {
3090 addAppsToAllApps(context, added);
3091 }
3092 }
3093
3094 if (modified != null) {
3095 final ArrayList<AppInfo> modifiedFinal = modified;
3096
3097 // Update the launcher db to reflect the changes
3098 for (AppInfo a : modifiedFinal) {
3099 ArrayList<ItemInfo> infos =
3100 getItemInfoForComponentName(a.componentName, mUser);
3101 for (ItemInfo i : infos) {
3102 if (isShortcutInfoUpdateable(i)) {
3103 ShortcutInfo info = (ShortcutInfo) i;
3104 info.title = a.title.toString();
3105 info.contentDescription = a.contentDescription;
3106 updateItemInDatabase(context, info);
3107 }
3108 }
3109 }
3110
3111 mHandler.post(new Runnable() {
3112 public void run() {
3113 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3114 if (callbacks == cb && cb != null) {
3115 callbacks.bindAppsUpdated(modifiedFinal);
3116 }
3117 }
3118 });
3119 }
3120
3121 final ArrayList<String> removedPackageNames =
3122 new ArrayList<String>();
3123 if (mOp == OP_REMOVE) {
3124 // Mark all packages in the broadcast to be removed
3125 removedPackageNames.addAll(Arrays.asList(packages));
3126 } else if (mOp == OP_UPDATE) {
3127 // Mark disabled packages in the broadcast to be removed
3128 final PackageManager pm = context.getPackageManager();
3129 for (int i=0; i<N; i++) {
3130 if (isPackageDisabled(context, packages[i], mUser)) {
3131 removedPackageNames.add(packages[i]);
3132 }
3133 }
3134 }
3135 // Remove all the components associated with this package
3136 for (String pn : removedPackageNames) {
3137 deletePackageFromDatabase(context, pn, mUser);
3138 }
3139 // Remove all the specific components
3140 for (AppInfo a : removedApps) {
3141 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
3142 deleteItemsFromDatabase(context, infos);
3143 }
3144 if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
3145 // Remove any queued items from the install queue
3146 String spKey = LauncherAppState.getSharedPreferencesKey();
3147 SharedPreferences sp =
3148 context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
3149 InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
3150 // Call the components-removed callback
3151 mHandler.post(new Runnable() {
3152 public void run() {
3153 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3154 if (callbacks == cb && cb != null) {
3155 callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
3156 }
3157 }
3158 });
3159 }
3160
3161 final ArrayList<Object> widgetsAndShortcuts =
3162 getSortedWidgetsAndShortcuts(context);
3163 mHandler.post(new Runnable() {
3164 @Override
3165 public void run() {
3166 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3167 if (callbacks == cb && cb != null) {
3168 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
3169 }
3170 }
3171 });
3172
3173 // Write all the logs to disk
3174 mHandler.post(new Runnable() {
3175 public void run() {
3176 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3177 if (callbacks == cb && cb != null) {
3178 callbacks.dumpLogsToLocalData();
3179 }
3180 }
3181 });
3182 }
3183 }
3184
3185 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
3186 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
3187 PackageManager packageManager = context.getPackageManager();
3188 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
3189 widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
3190
3191 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
3192 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
3193 Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
3194 return widgetsAndShortcuts;
3195 }
3196
3197 private static boolean isPackageDisabled(Context context, String packageName,
3198 UserHandleCompat user) {
3199 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3200 return !launcherApps.isPackageEnabledForProfile(packageName, user);
3201 }
3202
3203 public static boolean isValidPackageActivity(Context context, ComponentName cn,
3204 UserHandleCompat user) {
3205 if (cn == null) {
3206 return false;
3207 }
3208 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3209 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
3210 return false;
3211 }
3212 return launcherApps.isActivityEnabledForProfile(cn, user);
3213 }
3214
3215 public static boolean isValidPackage(Context context, String packageName,
3216 UserHandleCompat user) {
3217 if (packageName == null) {
3218 return false;
3219 }
3220 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3221 return launcherApps.isPackageEnabledForProfile(packageName, user);
3222 }
3223
3224 /**
3225 * Make an ShortcutInfo object for a restored application or shortcut item that points
3226 * to a package that is not yet installed on the system.
3227 */
3228 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
3229 int promiseType) {
3230 final ShortcutInfo info = new ShortcutInfo();
3231 info.user = UserHandleCompat.myUserHandle();
3232 mIconCache.getTitleAndIcon(info, intent, info.user, true);
3233
3234 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
3235 String title = (cursor != null) ? cursor.getString(titleIndex) : null;
3236 if (!TextUtils.isEmpty(title)) {
3237 info.title = title;
3238 }
3239 info.status = ShortcutInfo.FLAG_RESTORED_ICON;
3240 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
3241 if (TextUtils.isEmpty(info.title)) {
3242 info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
3243 }
3244 info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
3245 } else {
3246 throw new InvalidParameterException("Invalid restoreType " + promiseType);
3247 }
3248
3249 info.contentDescription = mUserManager.getBadgedLabelForUser(
3250 info.title.toString(), info.user);
3251 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3252 info.promisedIntent = intent;
3253 return info;
3254 }
3255
3256 /**
3257 * Make an Intent object for a restored application or shortcut item that points
3258 * to the market page for the item.
3259 */
3260 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
3261 ComponentName componentName = intent.getComponent();
3262 return getMarketIntent(componentName.getPackageName());
3263 }
3264
3265 static Intent getMarketIntent(String packageName) {
3266 return new Intent(Intent.ACTION_VIEW)
3267 .setData(new Uri.Builder()
3268 .scheme("market")
3269 .authority("details")
3270 .appendQueryParameter("id", packageName)
3271 .build());
3272 }
3273
3274 /**
3275 * This is called from the code that adds shortcuts from the intent receiver. This
3276 * doesn't have a Cursor, but
3277 */
3278 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3279 UserHandleCompat user, Context context) {
3280 return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
3281 }
3282
3283 /**
3284 * Make an ShortcutInfo object for a shortcut that is an application.
3285 *
3286 * If c is not null, then it will be used to fill in missing data like the title and icon.
3287 */
3288 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3289 UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
3290 HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
3291 if (user == null) {
3292 Log.d(TAG, "Null user found in getShortcutInfo");
3293 return null;
3294 }
3295
3296 ComponentName componentName = intent.getComponent();
3297 if (componentName == null) {
3298 Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
3299 return null;
3300 }
3301
3302 Intent newIntent = new Intent(intent.getAction(), null);
3303 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
3304 newIntent.setComponent(componentName);
3305 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
3306 if ((lai == null) && !allowMissingTarget) {
3307 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
3308 return null;
3309 }
3310
3311 final ShortcutInfo info = new ShortcutInfo();
3312
3313 // the resource -- This may implicitly give us back the fallback icon,
3314 // but don't worry about that. All we're doing with usingFallbackIcon is
3315 // to avoid saving lots of copies of that in the database, and most apps
3316 // have icons anyway.
3317 Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
3318
3319 // the db
3320 if (icon == null) {
3321 if (c != null) {
3322 icon = getIconFromCursor(c, iconIndex, context);
3323 }
3324 }
3325 // the fallback icon
3326 if (icon == null) {
3327 icon = mIconCache.getDefaultIcon(user);
3328 info.usingFallbackIcon = true;
3329 }
3330 info.setIcon(icon);
3331
3332 // From the cache.
3333 if (labelCache != null) {
3334 info.title = labelCache.get(componentName);
3335 }
3336
3337 // from the resource
3338 if (info.title == null && lai != null) {
3339 info.title = lai.getLabel();
3340 if (labelCache != null) {
3341 labelCache.put(componentName, info.title);
3342 }
3343 }
3344 // from the db
3345 if (info.title == null) {
3346 if (c != null) {
3347 info.title = c.getString(titleIndex);
3348 }
3349 }
3350 // fall back to the class name of the activity
3351 if (info.title == null) {
3352 info.title = componentName.getClassName();
3353 }
3354 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
3355 info.user = user;
3356 info.contentDescription = mUserManager.getBadgedLabelForUser(
3357 info.title.toString(), info.user);
3358 return info;
3359 }
3360
3361 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
3362 ItemInfoFilter f) {
3363 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
3364 for (ItemInfo i : infos) {
3365 if (i instanceof ShortcutInfo) {
3366 ShortcutInfo info = (ShortcutInfo) i;
3367 ComponentName cn = info.getTargetComponent();
3368 if (cn != null && f.filterItem(null, info, cn)) {
3369 filtered.add(info);
3370 }
3371 } else if (i instanceof FolderInfo) {
3372 FolderInfo info = (FolderInfo) i;
3373 for (ShortcutInfo s : info.contents) {
3374 ComponentName cn = s.getTargetComponent();
3375 if (cn != null && f.filterItem(info, s, cn)) {
3376 filtered.add(s);
3377 }
3378 }
3379 } else if (i instanceof LauncherAppWidgetInfo) {
3380 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
3381 ComponentName cn = info.providerName;
3382 if (cn != null && f.filterItem(null, info, cn)) {
3383 filtered.add(info);
3384 }
3385 }
3386 }
3387 return new ArrayList<ItemInfo>(filtered);
3388 }
3389
3390 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
3391 final UserHandleCompat user) {
3392 ItemInfoFilter filter = new ItemInfoFilter() {
3393 @Override
3394 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
3395 if (info.user == null) {
3396 return cn.equals(cname);
3397 } else {
3398 return cn.equals(cname) && info.user.equals(user);
3399 }
3400 }
3401 };
3402 return filterItemInfos(sBgItemsIdMap.values(), filter);
3403 }
3404
3405 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
3406 if (i instanceof ShortcutInfo) {
3407 ShortcutInfo info = (ShortcutInfo) i;
3408 // We need to check for ACTION_MAIN otherwise getComponent() might
3409 // return null for some shortcuts (for instance, for shortcuts to
3410 // web pages.)
3411 Intent intent = info.intent;
3412 ComponentName name = intent.getComponent();
3413 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
3414 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3415 return true;
3416 }
3417 // placeholder shortcuts get special treatment, let them through too.
3418 if (info.isPromise()) {
3419 return true;
3420 }
3421 }
3422 return false;
3423 }
3424
3425 /**
3426 * Make an ShortcutInfo object for a shortcut that isn't an application.
3427 */
3428 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
3429 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
3430 int titleIndex) {
3431
3432 Bitmap icon = null;
3433 final ShortcutInfo info = new ShortcutInfo();
3434 // Non-app shortcuts are only supported for current user.
3435 info.user = UserHandleCompat.myUserHandle();
3436 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3437
3438 // TODO: If there's an explicit component and we can't install that, delete it.
3439
3440 info.title = c.getString(titleIndex);
3441
3442 int iconType = c.getInt(iconTypeIndex);
3443 switch (iconType) {
3444 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
3445 String packageName = c.getString(iconPackageIndex);
3446 String resourceName = c.getString(iconResourceIndex);
3447 PackageManager packageManager = context.getPackageManager();
3448 info.customIcon = false;
3449 // the resource
3450 try {
3451 Resources resources = packageManager.getResourcesForApplication(packageName);
3452 if (resources != null) {
3453 final int id = resources.getIdentifier(resourceName, null, null);
3454 icon = Utilities.createIconBitmap(
3455 mIconCache.getFullResIcon(resources, id), context);
3456 }
3457 } catch (Exception e) {
3458 // drop this. we have other places to look for icons
3459 }
3460 // the db
3461 if (icon == null) {
3462 icon = getIconFromCursor(c, iconIndex, context);
3463 }
3464 // the fallback icon
3465 if (icon == null) {
3466 icon = mIconCache.getDefaultIcon(info.user);
3467 info.usingFallbackIcon = true;
3468 }
3469 break;
3470 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
3471 icon = getIconFromCursor(c, iconIndex, context);
3472 if (icon == null) {
3473 icon = mIconCache.getDefaultIcon(info.user);
3474 info.customIcon = false;
3475 info.usingFallbackIcon = true;
3476 } else {
3477 info.customIcon = true;
3478 }
3479 break;
3480 default:
3481 icon = mIconCache.getDefaultIcon(info.user);
3482 info.usingFallbackIcon = true;
3483 info.customIcon = false;
3484 break;
3485 }
3486 info.setIcon(icon);
3487 return info;
3488 }
3489
3490 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
3491 @SuppressWarnings("all") // suppress dead code warning
3492 final boolean debug = false;
3493 if (debug) {
3494 Log.d(TAG, "getIconFromCursor app="
3495 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
3496 }
3497 byte[] data = c.getBlob(iconIndex);
3498 try {
3499 return Utilities.createIconBitmap(
3500 BitmapFactory.decodeByteArray(data, 0, data.length), context);
3501 } catch (Exception e) {
3502 return null;
3503 }
3504 }
3505
3506 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
3507 int cellX, int cellY, boolean notify) {
3508 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
3509 if (info == null) {
3510 return null;
3511 }
3512 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
3513
3514 return info;
3515 }
3516
3517 /**
3518 * Attempts to find an AppWidgetProviderInfo that matches the given component.
3519 */
3520 static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
3521 ComponentName component) {
3522 List<AppWidgetProviderInfo> widgets =
3523 AppWidgetManager.getInstance(context).getInstalledProviders();
3524 for (AppWidgetProviderInfo info : widgets) {
3525 if (info.provider.equals(component)) {
3526 return info;
3527 }
3528 }
3529 return null;
3530 }
3531
3532 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
3533 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3534 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3535 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3536
3537 if (intent == null) {
3538 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3539 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3540 return null;
3541 }
3542
3543 Bitmap icon = null;
3544 boolean customIcon = false;
3545 ShortcutIconResource iconResource = null;
3546
3547 if (bitmap != null && bitmap instanceof Bitmap) {
3548 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
3549 customIcon = true;
3550 } else {
3551 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
3552 if (extra != null && extra instanceof ShortcutIconResource) {
3553 try {
3554 iconResource = (ShortcutIconResource) extra;
3555 final PackageManager packageManager = context.getPackageManager();
3556 Resources resources = packageManager.getResourcesForApplication(
3557 iconResource.packageName);
3558 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
3559 icon = Utilities.createIconBitmap(
3560 mIconCache.getFullResIcon(resources, id),
3561 context);
3562 } catch (Exception e) {
3563 Log.w(TAG, "Could not load shortcut icon: " + extra);
3564 }
3565 }
3566 }
3567
3568 final ShortcutInfo info = new ShortcutInfo();
3569
3570 // Only support intents for current user for now. Intents sent from other
3571 // users wouldn't get here without intent forwarding anyway.
3572 info.user = UserHandleCompat.myUserHandle();
3573 if (icon == null) {
3574 if (fallbackIcon != null) {
3575 icon = fallbackIcon;
3576 } else {
3577 icon = mIconCache.getDefaultIcon(info.user);
3578 info.usingFallbackIcon = true;
3579 }
3580 }
3581 info.setIcon(icon);
3582
3583 info.title = name;
3584 info.contentDescription = mUserManager.getBadgedLabelForUser(
3585 info.title.toString(), info.user);
3586 info.intent = intent;
3587 info.customIcon = customIcon;
3588 info.iconResource = iconResource;
3589
3590 return info;
3591 }
3592
3593 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
3594 int iconIndex) {
3595 // If apps can't be on SD, don't even bother.
3596 if (!mAppsCanBeOnRemoveableStorage) {
3597 return false;
3598 }
3599 // If this icon doesn't have a custom icon, check to see
3600 // what's stored in the DB, and if it doesn't match what
3601 // we're going to show, store what we are going to show back
3602 // into the DB. We do this so when we're loading, if the
3603 // package manager can't find an icon (for example because
3604 // the app is on SD) then we can use that instead.
3605 if (!info.customIcon && !info.usingFallbackIcon) {
3606 cache.put(info, c.getBlob(iconIndex));
3607 return true;
3608 }
3609 return false;
3610 }
3611 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
3612 boolean needSave = false;
3613 try {
3614 if (data != null) {
3615 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
3616 Bitmap loaded = info.getIcon(mIconCache);
3617 needSave = !saved.sameAs(loaded);
3618 } else {
3619 needSave = true;
3620 }
3621 } catch (Exception e) {
3622 needSave = true;
3623 }
3624 if (needSave) {
3625 Log.d(TAG, "going to save icon bitmap for info=" + info);
3626 // This is slower than is ideal, but this only happens once
3627 // or when the app is updated with a new icon.
3628 updateItemInDatabase(context, info);
3629 }
3630 }
3631
3632 /**
3633 * Return an existing FolderInfo object if we have encountered this ID previously,
3634 * or make a new one.
3635 */
3636 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
3637 // See if a placeholder was created for us already
3638 FolderInfo folderInfo = folders.get(id);
3639 if (folderInfo == null) {
3640 // No placeholder -- create a new instance
3641 folderInfo = new FolderInfo();
3642 folders.put(id, folderInfo);
3643 }
3644 return folderInfo;
3645 }
3646
3647 public static final Comparator<AppInfo> getAppNameComparator() {
3648 final Collator collator = Collator.getInstance();
3649 return new Comparator<AppInfo>() {
3650 public final int compare(AppInfo a, AppInfo b) {
3651 if (a.user.equals(b.user)) {
3652 int result = collator.compare(a.title.toString().trim(),
3653 b.title.toString().trim());
3654 if (result == 0) {
3655 result = a.componentName.compareTo(b.componentName);
3656 }
3657 return result;
3658 } else {
3659 // TODO Need to figure out rules for sorting
3660 // profiles, this puts work second.
3661 return a.user.toString().compareTo(b.user.toString());
3662 }
3663 }
3664 };
3665 }
3666 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
3667 = new Comparator<AppInfo>() {
3668 public final int compare(AppInfo a, AppInfo b) {
3669 if (a.firstInstallTime < b.firstInstallTime) return 1;
3670 if (a.firstInstallTime > b.firstInstallTime) return -1;
3671 return 0;
3672 }
3673 };
3674 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
3675 if (info.activityInfo != null) {
3676 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
3677 } else {
3678 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
3679 }
3680 }
3681 public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
3682 private Collator mCollator;
3683 private HashMap<Object, CharSequence> mLabelCache;
3684 ShortcutNameComparator(PackageManager pm) {
3685 mLabelCache = new HashMap<Object, CharSequence>();
3686 mCollator = Collator.getInstance();
3687 }
3688 ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
3689 mLabelCache = labelCache;
3690 mCollator = Collator.getInstance();
3691 }
3692 public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
3693 String labelA, labelB;
3694 ComponentName keyA = a.getComponentName();
3695 ComponentName keyB = b.getComponentName();
3696 if (mLabelCache.containsKey(keyA)) {
3697 labelA = mLabelCache.get(keyA).toString();
3698 } else {
3699 labelA = a.getLabel().toString().trim();
3700
3701 mLabelCache.put(keyA, labelA);
3702 }
3703 if (mLabelCache.containsKey(keyB)) {
3704 labelB = mLabelCache.get(keyB).toString();
3705 } else {
3706 labelB = b.getLabel().toString().trim();
3707
3708 mLabelCache.put(keyB, labelB);
3709 }
3710 return mCollator.compare(labelA, labelB);
3711 }
3712 };
3713 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
3714 private final AppWidgetManagerCompat mManager;
3715 private final PackageManager mPackageManager;
3716 private final HashMap<Object, String> mLabelCache;
3717 private final Collator mCollator;
3718
3719 WidgetAndShortcutNameComparator(Context context) {
3720 mManager = AppWidgetManagerCompat.getInstance(context);
3721 mPackageManager = context.getPackageManager();
3722 mLabelCache = new HashMap<Object, String>();
3723 mCollator = Collator.getInstance();
3724 }
3725 public final int compare(Object a, Object b) {
3726 String labelA, labelB;
3727 if (mLabelCache.containsKey(a)) {
3728 labelA = mLabelCache.get(a);
3729 } else {
3730 labelA = (a instanceof AppWidgetProviderInfo)
3731 ? mManager.loadLabel((AppWidgetProviderInfo) a)
3732 : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
3733 mLabelCache.put(a, labelA);
3734 }
3735 if (mLabelCache.containsKey(b)) {
3736 labelB = mLabelCache.get(b);
3737 } else {
3738 labelB = (b instanceof AppWidgetProviderInfo)
3739 ? mManager.loadLabel((AppWidgetProviderInfo) b)
3740 : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
3741 mLabelCache.put(b, labelB);
3742 }
3743 return mCollator.compare(labelA, labelB);
3744 }
3745 };
3746
3747 static boolean isValidProvider(AppWidgetProviderInfo provider) {
3748 return (provider != null) && (provider.provider != null)
3749 && (provider.provider.getPackageName() != null);
3750 }
3751
3752 public void dumpState() {
3753 Log.d(TAG, "mCallbacks=" + mCallbacks);
3754 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3755 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3756 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3757 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
3758 if (mLoaderTask != null) {
3759 mLoaderTask.dumpState();
3760 } else {
3761 Log.d(TAG, "mLoaderTask=null");
3762 }
3763 }
3764 }
|
1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.android.launcher3;
17
18 import android.app.SearchManager;
19 import android.appwidget.AppWidgetManager;
20 import android.appwidget.AppWidgetProviderInfo;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.ContentProviderClient;
24 import android.content.ContentProviderOperation;
25 import android.content.ContentResolver;
26 import android.content.ContentValues;
27 import android.content.Context;
28 import android.content.Intent.ShortcutIconResource;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ProviderInfo;
34 import android.content.pm.ResolveInfo;
35 import android.content.res.Configuration;
36 import android.content.res.Resources;
37 import android.database.Cursor;
38 import android.graphics.Bitmap;
39 import android.graphics.BitmapFactory;
40 import android.net.Uri;
41 import android.os.Environment;
42 import android.os.Handler;
43 import android.os.HandlerThread;
44 import android.os.Parcelable;
45 import android.os.Process;
46 import android.os.RemoteException;
47 import android.os.SystemClock;
48 import android.provider.BaseColumns;
49 import android.text.TextUtils;
50 import android.util.Log;
51 import android.util.Pair;
52 import com.android.launcher3.compat.AppWidgetManagerCompat;
53 import com.android.launcher3.compat.LauncherActivityInfoCompat;
54 import com.android.launcher3.compat.LauncherAppsCompat;
55 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
56 import com.android.launcher3.compat.PackageInstallerCompat;
57 import com.android.launcher3.compat.UserHandleCompat;
58 import com.android.launcher3.compat.UserManagerCompat;
59 import java.lang.ref.WeakReference;
60 import java.net.URISyntaxException;
61 import java.security.InvalidParameterException;
62 import java.text.Collator;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Collection;
66 import java.util.Collections;
67 import java.util.Comparator;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.Iterator;
71 import java.util.List;
72 import java.util.Map.Entry;
73 import java.util.Set;
74 import java.util.TreeMap;
75 import java.util.concurrent.atomic.AtomicBoolean;
76
77
78 /**
79 * Maintains in-memory state of the Launcher. It is expected that there should be only one
80 * LauncherModel object held in a static. Also provide APIs for updating the database state
81 * for the Launcher.
82 */
83 public class LauncherModel extends BroadcastReceiver implements LauncherAppsCompat.OnAppsChangedCallbackC🔵
84 static final boolean DEBUG_LOADERS = false;
85
86 private static final boolean DEBUG_RECEIVER = false;
87
88 private static final boolean REMOVE_UNRESTORED_ICONS = true;
89
90 static final String TAG = "Launcher.Model";
91
92 // true = use a "More Apps" folder for non-workspace apps on upgrade
93 // false = strew non-workspace apps across the workspace on upgrade
94 // true = use a "More Apps" folder for non-workspace apps on upgrade
95 // false = strew non-workspace apps across the workspace on upgrade
96 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
97
98 public static final int LOADER_FLAG_NONE = 0;
99
100 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
101
102 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
103
104 // batch size for the workspace icons
105 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
106
107 private static final long INVALID_SCREEN_ID = -1L;
108
109 private final boolean mAppsCanBeOnRemoveableStorage;
110
111 private final boolean mOldContentProviderExists;
112
113 private final LauncherAppState mApp;
114
115 private final Object mLock = new Object();
116
117 private DeferredHandler mHandler = new DeferredHandler();
118
119 private LoaderTask mLoaderTask;
120
121 private boolean mIsLoaderTaskRunning;
122
123 private volatile boolean mFlushingWorkerThread;
124
125 // Specific runnable types that are run on the main thread deferred handler, this allows us to
126 // clear all queued binding runnables when the Launcher activity is destroyed.
127 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
128
129 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
130
131 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
132
133 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
134
135 static {
136 sWorkerThread.start();
137 }
138
139 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
140
141 // We start off with everything not loaded. After that, we assume that
142 // our monitoring of the package manager provides all updates and we never
143 // need to do a requery. These are only ever touched from the loader thread.
144 private boolean mWorkspaceLoaded;
145
146 private boolean mAllAppsLoaded;
147
148 // When we are loading pages synchronously, we can't just post the binding of items on the side
149 // pages as this delays the rotation process. Instead, we wait for a callback from the first
150 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
151 // a normal load, we also clear this set of Runnables.
152 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
153
154 private WeakReference<Callbacks> mCallbacks;
155
156 // < only access in worker thread >
157 // < only access in worker thread >
158 AllAppsList mBgAllAppsList;
159
160 // The lock that must be acquired before referencing any static bg data structures. Unlike
161 // other locks, this one can generally be held long-term because we never expect any of these
162 // static data structures to be referenced outside of the worker thread except on the first
163 // load after configuration change.
164 static final Object sBgLock = new Object();
165
166 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
167 // LauncherModel to their ids
168 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
169
170 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
171 // created by LauncherModel that are directly on the home screen (however, no widgets or
172 // shortcuts within folders).
173 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
174
175 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
176 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
177
178 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
179 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
180
181 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
182 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
183
184 // sBgWorkspaceScreens is the ordered set of workspace screens.
185 // sBgWorkspaceScreens is the ordered set of workspace screens.
186 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
187
188 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
189 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages = new HashMap<UserHandleComp🔵
190
191 // </ only access in worker thread >
192 // </ only access in worker thread >
193
194 private IconCache mIconCache;
195
196 protected int mPreviousConfigMcc;
197
198 private final LauncherAppsCompat mLauncherApps;
199
200 private final UserManagerCompat mUserManager;
201
202 public interface Callbacks {
203 public boolean setLoadOnResume();
204
205 public int getCurrentWorkspaceScreen();
206
207 public void startBinding();
208
209 public abstract void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, boolean forceAn🔵
210
211 public void bindScreens(ArrayList<Long> orderedScreenIds);
212
213 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
214
215 public abstract void bindFolders(HashMap<Long, FolderInfo> folders);
216
217 public void finishBindingItems(boolean upgradePath);
218
219 public abstract void bindAppWidget(LauncherAppWidgetInfo info);
220
221 public abstract void bindAllApplications(ArrayList<AppInfo> apps);
222
223 public abstract void bindAppsAdded(ArrayList<Long> newScreens, ArrayList<ItemInfo> addNotAnimated🔵
224
225 public abstract void bindAppsUpdated(ArrayList<AppInfo> apps);
226
227 public void bindAppsRestored(ArrayList<AppInfo> apps);
228
229 public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
230
231 public void updatePackageBadge(String packageName);
232
233 public abstract void bindComponentsRemoved(ArrayList<String> packageNames, ArrayList<AppInfo> app🔵
234
235 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
236
237 public void bindSearchablesChanged();
238
239 public boolean isAllAppsButtonRank(int rank);
240
241 public void onPageBoundSynchronously(int page);
242
243 public void dumpLogsToLocalData();
244 }
245
246 public interface ItemInfoFilter {
247 public abstract boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
248 }
249
250 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
251 Context context = app.getContext();
252 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
253 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
254 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
255 // resource string.
256 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
257 ProviderInfo providerInfo = context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY,🔵
258 ProviderInfo redirectProvider = context.getPackageManager().resolveContentProvider(redirectAuthor🔵
259 Log.d(TAG, "Old launcher provider: " + oldProvider);
260 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
261 if (mOldContentProviderExists) {
262 Log.d(TAG, "Old launcher provider exists.");
263 } else {
264 Log.d(TAG, "Old launcher provider does not exist.");
265 }
266 mApp = app;
267 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
268 mIconCache = iconCache;
269 final Resources res = context.getResources();
270 Configuration config = res.getConfiguration();
271 mPreviousConfigMcc = config.mcc;
272 mLauncherApps = LauncherAppsCompat.getInstance(context);
273 mUserManager = UserManagerCompat.getInstance(context);
274 }
275
276 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
277 * posted on the main thread handler. */
278 private void runOnMainThread(Runnable r) {
279 runOnMainThread(r, 0);
280 }
281
282 private void runOnMainThread(Runnable r, int type) {
283 if (sWorkerThread.getThreadId() == Process.myTid()) {
284 // If we are on the worker thread, post onto the main handler
285 mHandler.post(r);
286 } else {
287 r.run();
288 }
289 }
290
291 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
292 * posted on the worker thread handler. */
293 private static void runOnWorkerThread(Runnable r) {
294 if (sWorkerThread.getThreadId() == Process.myTid()) {
295 r.run();
296 } else {
297 // If we are not on the worker thread, then post to the worker handler
298 sWorker.post(r);
299 }
300 }
301
302 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
303 return mOldContentProviderExists && (!launcher.isLauncherPreinstalled());
304 }
305
306 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy, long screen) {
307 LauncherAppState app = LauncherAppState.getInstance();
308 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
309 final int xCount = ((int) (grid.numColumns));
310 final int yCount = ((int) (grid.numRows));
311 boolean[][] occupied = new boolean[xCount][yCount];
312 int cellX;
313 int cellY;
314 int spanX;
315 int spanY;
316 for (int i = 0; i < items.size(); ++i) {
317 final ItemInfo item = items.get(i);
318 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
319 if (item.screenId == screen) {
320 cellX = item.cellX;
321 cellY = item.cellY;
322 spanX = item.spanX;
323 spanY = item.spanY;
324 for (int x = cellX; ((0 <= x) && (x < (cellX + spanX))) && (x < xCount); x++) {
325 for (int y = cellY; ((0 <= y) && (y < (cellY + spanY))) && (y < yCount); y++) {
326 occupied[x][y] = true;
327 }
328 }
329 }
330 }
331 }
332 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
333 }
334
335 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name, Intent launchIntent🔵
336 // Lock on the app so that we don't try and get the items while apps are being added
337 LauncherAppState app = LauncherAppState.getInstance();
338 LauncherModel model = app.getModel();
339 boolean found = false;
340 synchronized(app) {
341 if (sWorkerThread.getThreadId() != Process.myTid()) {
342 // Flush the LauncherModel worker thread, so that if we just did another
343 // processInstallShortcut, we give it time for its shortcut to get added to the
344 // database (getItemsInLocalCoordinates reads the database)
345 model.flushWorkerThread();
346 }
347 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
348 // Try adding to the workspace screens incrementally, starting at the default or center
349 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
350 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
351 int count = workspaceScreens.size();
352 for (int screen = firstScreenIndex; (screen < count) && (!found); screen++) {
353 int[] tmpCoordinates = new int[2];
354 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates, workspaceScreens.get(screen🔵
355 // Update the Launcher db
356 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
357 }
358 }
359 }
360 return null;
361 }
362
363 public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
364 // Process the updated package state
365 Runnable r = new Runnable() {
366 public void run() {
367 Callbacks callbacks = (mCallbacks != null) ? mCallbacks.get() : null;
368 if (callbacks != null) {
369 callbacks.updatePackageState(installInfo);
370 }
371 }
372 };
373 mHandler.post(r);
374 }
375
376 public void updatePackageBadge(final String packageName) {
377 // Process the updated package badge
378 Runnable r = new Runnable() {
379 public void run() {
380 Callbacks callbacks = (mCallbacks != null) ? mCallbacks.get() : null;
381 if (callbacks != null) {
382 callbacks.updatePackageBadge(packageName);
383 }
384 }
385 };
386 mHandler.post(r);
387 }
388
389 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
390 final Callbacks callbacks = (mCallbacks != null) ? mCallbacks.get() : null;
391 if (allAppsApps == null) {
392 throw new RuntimeException("allAppsApps must not be null");
393 }
394 if (allAppsApps.isEmpty()) {
395 return;
396 }
397 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
398 Iterator<AppInfo> iter = allAppsApps.iterator();
399 while (iter.hasNext()) {
400 ItemInfo a = iter.next();
401 if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
402 restoredAppsFinal.add(((AppInfo) (a)));
403 }
404 }
405 // Process the newly added applications and add them to the database first
406 Runnable r = new Runnable() {
407 public void run() {
408 runOnMainThread(new Runnable() {
409 public void run() {
410 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
411 if ((callbacks == cb) && (cb != null)) {
412 if (!restoredAppsFinal.isEmpty()) {
413 for (AppInfo info : restoredAppsFinal) {
414 final Intent intent = info.getIntent();
415 if (intent != null) {
416 mIconCache.deletePreloadedIcon(intent.getComponent(), info.user);
417 }
418 }
419 callbacks.bindAppsUpdated(restoredAppsFinal);
420 }
421 callbacks.bindAppsAdded(null, null, null, allAppsApps);
422 }
423 }
424 });
425 }
426 };
427 runOnWorkerThread(r);
428 }
429
430 public void addAndBindAddedWorkspaceApps(final Context context, final ArrayList<ItemInfo> workspaceAp🔵
431 final Callbacks callbacks = (mCallbacks != null) ? mCallbacks.get() : null;
432 if (workspaceApps == null) {
433 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
434 }
435 if (workspaceApps.isEmpty()) {
436 return;
437 }
438 // Process the newly added applications and add them to the database first
439 Runnable r = new Runnable() {
440 public void run() {
441 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
442 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
443 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
444 // Get the list of workspace screens. We need to append to this list and
445 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
446 // called.
447 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
448 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
449 for (Integer i : orderedScreens.keySet()) {
450 long screenId = orderedScreens.get(i);
451 workspaceScreens.add(screenId);
452 }
453 synchronized(sBgLock) {
454 Iterator<ItemInfo> iter = workspaceApps.iterator();
455 while (iter.hasNext()) {
456 ItemInfo a = iter.next();
457 final String name = a.title.toString();
458 final Intent launchIntent = a.getIntent();
459 // Short-circuit this logic if the icon exists somewhere on the workspace
460 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
461 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
462 if ((a instanceof AppInfo) && LauncherModel.appWasPromise(context, launchInte🔵
463 restoredAppsFinal.add(((AppInfo) (a)));
464 }
465 continue;
466 }
467 // Add this icon to the db, creating a new page if necessary. If there
468 // is only the empty page then we just add items to the first page.
469 // Otherwise, we add them to the next pages.
470 int startSearchPageIndex = (workspaceScreens.isEmpty()) ? 0 : 1;
471 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context, name🔵
472 if (coords == null) {
473 LauncherProvider lp = LauncherAppState.getLauncherProvider();
474 // If we can't find a valid position, then just add a new screen.
475 // This takes time so we need to re-queue the add until the new
476 // page is added. Create as many screens as necessary to satisfy
477 // the startSearchPageIndex.
478 int numPagesToAdd = Math.max(1, (startSearchPageIndex + 1) - workspaceScreens🔵
479 while (numPagesToAdd > 0) {
480 long screenId = lp.generateNewScreenId();
481 // Save the screen id for binding in the workspace
482 workspaceScreens.add(screenId);
483 addedWorkspaceScreensFinal.add(screenId);
484 numPagesToAdd--;
485 }
486 // Find the coordinate again
487 coords = LauncherModel.findNextAvailableIconSpace(context, name, launchIntent🔵
488 }
489 if (coords == null) {
490 throw new RuntimeException("Coordinates should not be null");
491 }
492 ShortcutInfo shortcutInfo;
493 if (a instanceof ShortcutInfo) {
494 shortcutInfo = ((ShortcutInfo) (a));
495 } else if (a instanceof AppInfo) {
496 shortcutInfo = ((AppInfo) (a)).makeShortcut();
497 } else {
498 throw new RuntimeException("Unexpected info type");
499 }
500 // Add the shortcut to the db
501 addItemToDatabase(context, shortcutInfo, LauncherSettings.Favorites.CONTAINER_DES🔵
502 // Save the ShortcutInfo for binding in the workspace
503 addedShortcutsFinal.add(shortcutInfo);
504 }
505 }
506 // Update the workspace screens
507 updateWorkspaceScreenOrder(context, workspaceScreens);
508 if (!addedShortcutsFinal.isEmpty()) {
509 runOnMainThread(new Runnable() {
510 public void run() {
511 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
512 if ((callbacks == cb) && (cb != null)) {
513 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
514 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
515 if (!addedShortcutsFinal.isEmpty()) {
516 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 🔵
517 long lastScreenId = info.screenId;
518 for (ItemInfo i : addedShortcutsFinal) {
519 if (i.screenId == lastScreenId) {
520 addAnimated.add(i);
521 } else {
522 addNotAnimated.add(i);
523 }
524 }
525 }
526 callbacks.bindAppsAdded(addedWorkspaceScreensFinal, addNotAnimated, addAn🔵
527 if (!restoredAppsFinal.isEmpty()) {
528 callbacks.bindAppsUpdated(restoredAppsFinal);
529 }
530 }
531 }
532 });
533 }
534 }
535 };
536 runOnWorkerThread(r);
537 }
538
539 public void unbindItemInfosAndClearQueuedBindRunnables() {
540 if (sWorkerThread.getThreadId() == Process.myTid()) {
541 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " + "mai🔵
542 }
543 // Clear any deferred bind runnables
544 synchronized(mDeferredBindRunnables) {
545 mDeferredBindRunnables.clear();
546 }
547 // Remove any queued bind runnables
548 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
549 // Unbind all the workspace items
550 unbindWorkspaceItemsOnMainThread();
551 }
552
553 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
554 void unbindWorkspaceItemsOnMainThread() {
555 // Ensure that we don't use the same workspace items data structure on the main thread
556 // by making a copy of workspace items first.
557 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
558 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
559 synchronized(sBgLock) {
560 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
561 tmpAppWidgets.addAll(sBgAppWidgets);
562 }
563 Runnable r = new Runnable() {
564 @Override
565 public void run() {
566 for (ItemInfo item : tmpWorkspaceItems) {
567 item.unbind();
568 }
569 for (ItemInfo item : tmpAppWidgets) {
570 item.unbind();
571 }
572 }
573 };
574 runOnMainThread(r);
575 }
576
577 /**
578 * Adds an item to the DB if it was not created previously, or move it to a new
579 * <container, screen, cellX, cellY>
580 */
581 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container, long screenId, in🔵
582 if (item.container == ItemInfo.NO_ID) {
583 // From all apps
584 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
585 } else {
586 // From somewhere else
587 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
588 }
589 }
590
591 static void checkItemInfoLocked(final long itemId, final ItemInfo item, StackTraceElement[] stackTrac🔵
592 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
593 if ((modelItem != null) && (item != modelItem)) {
594 // check all the data is consistent
595 if ((modelItem instanceof ShortcutInfo) && (item instanceof ShortcutInfo)) {
596 ShortcutInfo modelShortcut = ((ShortcutInfo) (modelItem));
597 ShortcutInfo shortcut = ((ShortcutInfo) (item));
598 if ((((((((((modelShortcut.title.toString().equals(shortcut.title.toString()) && modelSho🔵
599 // For all intents and purposes, this is the same object
600 return;
601 }
602 }
603 // the modelItem needs to match up perfectly with item if our model is
604 // to be consistent with the database-- for now, just require
605 // modelItem == item or the equality check above
606 String msg = ((("item: " + (item != null ? item.toString() : "null")) + "modelItem: ") + (mod🔵
607 RuntimeException e = new RuntimeException(msg);
608 if (stackTrace != null) {
609 e.setStackTrace(stackTrace);
610 }
611 throw e;
612 }
613 }
614
615 static void checkItemInfo(final ItemInfo item) {
616 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
617 final long itemId = item.id;
618 Runnable r = new Runnable() {
619 public void run() {
620 synchronized(sBgLock) {
621 checkItemInfoLocked(itemId, item, stackTrace);
622 }
623 }
624 };
625 runOnWorkerThread(r);
626 }
627
628 static void updateItemInDatabaseHelper(Context context, final ContentValues values, final ItemInfo it🔵
629 final long itemId = item.id;
630 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
631 final ContentResolver cr = context.getContentResolver();
632 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
633 Runnable r = new Runnable() {
634 public void run() {
635 cr.update(uri, values, null, null);
636 updateItemArrays(item, itemId, stackTrace);
637 }
638 };
639 runOnWorkerThread(r);
640 }
641
642 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList, f🔵
643 final ContentResolver cr = context.getContentResolver();
644 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
645 Runnable r = new Runnable() {
646 public void run() {
647 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
648 int count = items.size();
649 for (int i = 0; i < count; i++) {
650 ItemInfo item = items.get(i);
651 final long itemId = item.id;
652 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
653 ContentValues values = valuesList.get(i);
654 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
655 updateItemArrays(item, itemId, stackTrace);
656 }
657 try {
658 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
659 } catch (java.lang.Exception e) {
660 e.printStackTrace();
661 }
662 }
663 };
664 runOnWorkerThread(r);
665 }
666
667 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
668 // Lock on mBgLock *after* the db operation
669 synchronized(sBgLock) {
670 checkItemInfoLocked(itemId, item, stackTrace);
671 if ((item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) && (item.container != La🔵
672 // Item is in a folder, make sure this folder exists
673 if (!sBgFolders.containsKey(item.container)) {
674 // An items container is being set to a that of an item which is not in
675 // the list of Folders.
676 String msg = ((("item: " + item) + " container being set to: ") + item.container) + "🔵
677 Log.e(TAG, msg);
678 }
679 }
680 // Items are added/removed from the corresponding FolderInfo elsewhere, such
681 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
682 // that are on the desktop, as appropriate
683 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
684 if ((modelItem != null) && ((modelItem.container == LauncherSettings.Favorites.CONTAINER_DESK🔵
685 switch (modelItem.itemType) {
686 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION :
687 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT :
688 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER :
689 if (!sBgWorkspaceItems.contains(modelItem)) {
690 sBgWorkspaceItems.add(modelItem);
691 }
692 break;
693 default :
694 break;
695 }
696 } else {
697 sBgWorkspaceItems.remove(modelItem);
698 }
699 }
700 }
701
702 public void flushWorkerThread() {
703 mFlushingWorkerThread = true;
704 Runnable waiter = new Runnable() {
705 public void run() {
706 synchronized (this) {
707 notifyAll();
708 mFlushingWorkerThread = false;
709 }
710 }
711 };
712
713 synchronized(waiter) {
714 runOnWorkerThread(waiter);
715 if (mLoaderTask != null) {
716 synchronized(mLoaderTask) {
717 mLoaderTask.notify();
718 }
719 }
720 boolean success = false;
721 while (!success) {
722 try {
723 waiter.wait();
724 success = true;
725 } catch (InterruptedException e) {
726 }
727 }
728 }
729 }
730
731 /**
732 * Move an item in the DB to a new <container, screen, cellX, cellY>
733 */
734 static void moveItemInDatabase(Context context, final ItemInfo item, final long container, final long🔵
735 item.container = container;
736 item.cellX = cellX;
737 item.cellY = cellY;
738 // We store hotseat items in canonical form which is this orientation invariant position
739 // in the hotseat
740 if (((context instanceof Launcher) && (screenId < 0)) && (container == LauncherSettings.Favorites🔵
741 item.screenId = ((Launcher) (context)).getHotseat().getOrderInHotseat(cellX, cellY);
742 } else {
743 item.screenId = screenId;
744 }
745 final ContentValues values = new ContentValues();
746 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
747 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
748 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
749 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
750 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
751 }
752
753 /**
754 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
755 * cellX, cellY have already been updated on the ItemInfos.
756 */
757 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items, final long containe🔵
758 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
759 int count = items.size();
760 for (int i = 0; i < count; i++) {
761 ItemInfo item = items.get(i);
762 item.container = container;
763 // We store hotseat items in canonical form which is this orientation invariant position
764 // in the hotseat
765 if (((context instanceof Launcher) && (screen < 0)) && (container == LauncherSettings.Favorit🔵
766 item.screenId = ((Launcher) (context)).getHotseat().getOrderInHotseat(item.cellX, item.ce🔵
767 } else {
768 item.screenId = screen;
769 }
770 final ContentValues values = new ContentValues();
771 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
772 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
773 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
774 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
775 contentValues.add(values);
776 }
777 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
778 }
779
780 /**
781 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
782 */
783 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container, final lo🔵
784 item.container = container;
785 item.cellX = cellX;
786 item.cellY = cellY;
787 item.spanX = spanX;
788 item.spanY = spanY;
789 // We store hotseat items in canonical form which is this orientation invariant position
790 // in the hotseat
791 if (((context instanceof Launcher) && (screenId < 0)) && (container == LauncherSettings.Favorites🔵
792 item.screenId = ((Launcher) (context)).getHotseat().getOrderInHotseat(cellX, cellY);
793 } else {
794 item.screenId = screenId;
795 }
796 final ContentValues values = new ContentValues();
797 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
798 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
799 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
800 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
801 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
802 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
803 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
804 }
805
806 /**
807 * Update an item to the database in a specified container.
808 */
809 static void updateItemInDatabase(Context context, final ItemInfo item) {
810 final ContentValues values = new ContentValues();
811 item.onAddToDatabase(context, values);
812 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
813 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
814 }
815
816 /**
817 * Returns true if the shortcuts already exists in the database.
818 * we identify a shortcut by its title and intent.
819 */
820 static boolean shortcutExists(Context context, String title, Intent intent) {
821 final ContentResolver cr = context.getContentResolver();
822 final Intent intentWithPkg;
823 final Intent intentWithoutPkg;
824 if (intent.getComponent() != null) {
825 // If component is not null, an intent with null package will produce
826 // the same result and should also be a match.
827 if (intent.getPackage() != null) {
828 intentWithPkg = intent;
829 intentWithoutPkg = new Intent(intent).setPackage(null);
830 } else {
831 intentWithPkg = new Intent(intent).setPackage(intent.getComponent().getPackageName());
832 intentWithoutPkg = intent;
833 }
834 } else {
835 intentWithPkg = intent;
836 intentWithoutPkg = intent;
837 }
838 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[]{ "title", "intent" }, "t🔵
839 boolean result = false;
840 try {
841 result = c.moveToFirst();
842 } finally {
843 c.close();
844 }
845 return result;
846 }
847
848 /**
849 * Returns true if the promise shortcuts with the same package name exists on the workspace.
850 */
851 static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
852 final ComponentName component = intent.getComponent();
853 if (component == null) {
854 return false;
855 }
856 return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
857 }
858
859 /**
860 * Returns an ItemInfo array containing all the items in the LauncherModel.
861 * The ItemInfo.id is not set through this function.
862 */
863 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
864 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
865 final ContentResolver cr = context.getContentResolver();
866 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[]{ LauncherSettings.Favori🔵
867 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
868 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
869 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
870 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
871 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
872 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
873 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
874 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
875 UserManagerCompat userManager = UserManagerCompat.getInstance(context);
876 try {
877 while (c.moveToNext()) {
878 ItemInfo item = new ItemInfo();
879 item.cellX = c.getInt(cellXIndex);
880 item.cellY = c.getInt(cellYIndex);
881 item.spanX = Math.max(1, c.getInt(spanXIndex));
882 item.spanY = Math.max(1, c.getInt(spanYIndex));
883 item.container = c.getInt(containerIndex);
884 item.itemType = c.getInt(itemTypeIndex);
885 item.screenId = c.getInt(screenIndex);
886 long serialNumber = c.getInt(profileIdIndex);
887 item.user = userManager.getUserForSerialNumber(serialNumber);
888 // Skip if user has been deleted.
889 if (item.user != null) {
890 items.add(item);
891 }
892 }
893 } catch (java.lang.Exception e) {
894 items.clear();
895 } finally {
896 c.close();
897 }
898 return items;
899 }
900
901 /**
902 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
903 */
904 FolderInfo getFolderById(Context context, HashMap<Long, FolderInfo> folderList, long id) {
905 final ContentResolver cr = context.getContentResolver();
906 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null, "_id=? and (itemType=? or itemT🔵
907 try {
908 if (c.moveToFirst()) {
909 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
910 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
911 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
912 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
913 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
914 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
915 FolderInfo folderInfo = null;
916 switch (c.getInt(itemTypeIndex)) {
917 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER :
918 folderInfo = findOrMakeFolder(folderList, id);
919 break;
920 }
921 folderInfo.title = c.getString(titleIndex);
922 folderInfo.id = id;
923 folderInfo.container = c.getInt(containerIndex);
924 folderInfo.screenId = c.getInt(screenIndex);
925 folderInfo.cellX = c.getInt(cellXIndex);
926 folderInfo.cellY = c.getInt(cellYIndex);
927 return folderInfo;
928 }
929 } finally {
930 c.close();
931 }
932 return null;
933 }
934
935 /**
936 * Add an item to the database in a specified container. Sets the container, screen, cellX and
937 * cellY fields of the item. Also assigns an ID to the item.
938 */
939 static void addItemToDatabase(Context context, final ItemInfo item, final long container, final long 🔵
940 item.container = container;
941 item.cellX = cellX;
942 item.cellY = cellY;
943 // We store hotseat items in canonical form which is this orientation invariant position
944 // in the hotseat
945 if (((context instanceof Launcher) && (screenId < 0)) && (container == LauncherSettings.Favorites🔵
946 item.screenId = ((Launcher) (context)).getHotseat().getOrderInHotseat(cellX, cellY);
947 } else {
948 item.screenId = screenId;
949 }
950 final ContentValues values = new ContentValues();
951 final ContentResolver cr = context.getContentResolver();
952 item.onAddToDatabase(context, values);
953 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
954 values.put(LauncherSettings.Favorites._ID, item.id);
955 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
956 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
957 Runnable r = new Runnable() {
958 public void run() {
959 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI : LauncherSettings.Favorites.CO🔵
960 // Lock on mBgLock *after* the db operation
961 synchronized(sBgLock) {
962 checkItemInfoLocked(item.id, item, stackTrace);
963 sBgItemsIdMap.put(item.id, item);
964 switch (item.itemType) {
965 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER :
966 sBgFolders.put(item.id, ((FolderInfo) (item)));
967 // Fall through
968 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION :
969 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT :
970 if ((item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) || (item🔵
971 sBgWorkspaceItems.add(item);
972 } else if (!sBgFolders.containsKey(item.container)) {
973 // Adding an item to a folder that doesn't exist.
974 String msg = (("adding item: " + item) + " to a folder that ") + " doesn'🔵
975 Log.e(TAG, msg);
976 }
977 break;
978 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET :
979 sBgAppWidgets.add(((LauncherAppWidgetInfo) (item)));
980 break;
981 }
982 }
983 }
984 };
985 runOnWorkerThread(r);
986 }
987
988 /**
989 * Creates a new unique child id, for a given cell span across all layouts.
990 */
991 static int getCellLayoutChildId(
992 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
993 return (((int) container & 0xFF) << 24)
994 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
995 }
996
997 private static ArrayList<ItemInfo> getItemsByPackageName(final String pn, final UserHandleCompat user🔵
998 ItemInfoFilter filter = new ItemInfoFilter() {
999 @Override
1000 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
1001 return cn.getPackageName().equals(pn) && info.user.equals(user);
1002 }
1003 };
1004 return filterItemInfos(sBgItemsIdMap.values(), filter);
1005 }
1006
1007 /**
1008 * Removes all the items from the database corresponding to the specified package.
1009 */
1010 static void deletePackageFromDatabase(Context context, final String pn,
1011 final UserHandleCompat user) {
1012 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
1013 }
1014
1015 /**
1016 * Removes the specified item from the database
1017 * @param context
1018 * @param item
1019 */
1020 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
1021 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1022 items.add(item);
1023 deleteItemsFromDatabase(context, items);
1024 }
1025
1026 /**
1027 * Removes the specified items from the database
1028 * @param context
1029 * @param item
1030 */
1031 static void deleteItemsFromDatabase(Context context, final ArrayList<ItemInfo> items) {
1032 final ContentResolver cr = context.getContentResolver();
1033 Runnable r = new Runnable() {
1034 public void run() {
1035 for (ItemInfo item : items) {
1036 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
1037 cr.delete(uri, null, null);
1038 // Lock on mBgLock *after* the db operation
1039 synchronized(sBgLock) {
1040 switch (item.itemType) {
1041 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER :
1042 sBgFolders.remove(item.id);
1043 for (ItemInfo info : sBgItemsIdMap.values()) {
1044 if (info.container == item.id) {
1045 // We are deleting a folder which still contains items that
1046 // think they are contained by that folder.
1047 String msg = (((("deleting a folder (" + item) + ") which still "🔵
1048 Log.e(TAG, msg);
1049 }
1050 }
1051 sBgWorkspaceItems.remove(item);
1052 break;
1053 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION :
1054 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT :
1055 sBgWorkspaceItems.remove(item);
1056 break;
1057 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET :
1058 sBgAppWidgets.remove(((LauncherAppWidgetInfo) (item)));
1059 break;
1060 }
1061 sBgItemsIdMap.remove(item.id);
1062 sBgDbIconCache.remove(item);
1063 }
1064 }
1065 }
1066 };
1067 runOnWorkerThread(r);
1068 }
1069
1070 /**
1071 * Update the order of the workspace screens in the database. The array list contains
1072 * a list of screen ids in the order that they should appear.
1073 */
1074 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
1075 // Log to disk
1076 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
1077 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
1078 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
1079 final ContentResolver cr = context.getContentResolver();
1080 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1081 // Remove any negative screen ids -- these aren't persisted
1082 Iterator<Long> iter = screensCopy.iterator();
1083 while (iter.hasNext()) {
1084 long id = iter.next();
1085 if (id < 0) {
1086 iter.remove();
1087 }
1088 }
1089 Runnable r = new Runnable() {
1090 @Override
1091 public void run() {
1092 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1093 // Clear the table
1094 ops.add(ContentProviderOperation.newDelete(uri).build());
1095 int count = screensCopy.size();
1096 for (int i = 0; i < count; i++) {
1097 ContentValues v = new ContentValues();
1098 long screenId = screensCopy.get(i);
1099 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1100 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1101 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
1102 }
1103 try {
1104 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1105 } catch (java.lang.Exception ex) {
1106 throw new RuntimeException(ex);
1107 }
1108 synchronized(sBgLock) {
1109 sBgWorkspaceScreens.clear();
1110 sBgWorkspaceScreens.addAll(screensCopy);
1111 }
1112 }
1113 };
1114 runOnWorkerThread(r);
1115 }
1116
1117 /**
1118 * Remove the contents of the specified folder from the database
1119 */
1120 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
1121 final ContentResolver cr = context.getContentResolver();
1122 Runnable r = new Runnable() {
1123 public void run() {
1124 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1125 // Lock on mBgLock *after* the db operation
1126 synchronized(sBgLock) {
1127 sBgItemsIdMap.remove(info.id);
1128 sBgFolders.remove(info.id);
1129 sBgDbIconCache.remove(info);
1130 sBgWorkspaceItems.remove(info);
1131 }
1132 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, (LauncherSettings.Favor🔵
1133 // Lock on mBgLock *after* the db operation
1134 synchronized(sBgLock) {
1135 for (ItemInfo childInfo : info.contents) {
1136 sBgItemsIdMap.remove(childInfo.id);
1137 sBgDbIconCache.remove(childInfo);
1138 }
1139 }
1140 }
1141 };
1142 runOnWorkerThread(r);
1143 }
1144
1145 /**
1146 * Set this as the current Launcher activity object for the loader.
1147 */
1148 public void initialize(Callbacks callbacks) {
1149 synchronized (mLock) {
1150 mCallbacks = new WeakReference<Callbacks>(callbacks);
1151 }
1152 }
1153
1154 @Override
1155 public void onPackageChanged(String packageName, UserHandleCompat user) {
1156 int op = PackageUpdatedTask.OP_UPDATE;
1157 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[]{ packageName }, user));
1158 }
1159
1160 @Override
1161 public void onPackageRemoved(String packageName, UserHandleCompat user) {
1162 int op = PackageUpdatedTask.OP_REMOVE;
1163 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[]{ packageName }, user));
1164 }
1165
1166 @Override
1167 public void onPackageAdded(String packageName, UserHandleCompat user) {
1168 int op = PackageUpdatedTask.OP_ADD;
1169 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[]{ packageName }, user));
1170 }
1171
1172 @Override
1173 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing) {
1174 if (!replacing) {
1175 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames, user));
1176 if (mAppsCanBeOnRemoveableStorage) {
1177 // Only rebind if we support removable storage. It catches the
1178 // case where
1179 // apps on the external sd card need to be reloaded
1180 startLoaderFromBackground();
1181 }
1182 } else {
1183 // If we are replacing then just update the packages in the list
1184 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user🔵
1185 }
1186 }
1187
1188 @Override
1189 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing) {
1190 if (!replacing) {
1191 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE, packageNames,🔵
1192 }
1193 }
1194
1195 /**
1196 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1197 * ACTION_PACKAGE_CHANGED.
1198 */
1199 @Override
1200 public void onReceive(Context context, Intent intent) {
1201 if (DEBUG_RECEIVER) {
1202 Log.d(TAG, "onReceive intent=" + intent);
1203 }
1204 final String action = intent.getAction();
1205 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
1206 // If we have changed locale we need to clear out the labels in all apps/workspace.
1207 forceReload();
1208 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1209 // Check if configuration change was an mcc/mnc change which would affect app resources
1210 // and we would need to clear out the labels in all apps/workspace. Same handling as
1211 // above for ACTION_LOCALE_CHANGED
1212 Configuration currentConfig = context.getResources().getConfiguration();
1213 if (mPreviousConfigMcc != currentConfig.mcc) {
1214 Log.d(TAG, (("Reload apps on config change. curr_mcc:" + currentConfig.mcc) + " prevmcc:"🔵
1215 forceReload();
1216 }
1217 // Update previousConfig
1218 mPreviousConfigMcc = currentConfig.mcc;
1219 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) || SearchManager.IN🔵
1220 if (mCallbacks != null) {
1221 Callbacks callbacks = mCallbacks.get();
1222 if (callbacks != null) {
1223 callbacks.bindSearchablesChanged();
1224 }
1225 }
1226 }
1227 }
1228
1229 void forceReload() {
1230 resetLoadedState(true, true);
1231 // Do this here because if the launcher activity is running it will be restarted.
1232 // If it's not running startLoaderFromBackground will merely tell it that it needs
1233 // to reload.
1234 startLoaderFromBackground();
1235 }
1236
1237 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1238 synchronized (mLock) {
1239 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1240 // mWorkspaceLoaded to true later
1241 stopLoaderLocked();
1242 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1243 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1244 }
1245 }
1246
1247 /**
1248 * When the launcher is in the background, it's possible for it to miss paired
1249 * configuration changes. So whenever we trigger the loader from the background
1250 * tell the launcher that it needs to re-run the loader when it comes back instead
1251 * of doing it now.
1252 */
1253 public void startLoaderFromBackground() {
1254 boolean runLoader = false;
1255 if (mCallbacks != null) {
1256 Callbacks callbacks = mCallbacks.get();
1257 if (callbacks != null) {
1258 // Only actually run the loader if they're not paused.
1259 if (!callbacks.setLoadOnResume()) {
1260 runLoader = true;
1261 }
1262 }
1263 }
1264 if (runLoader) {
1265 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
1266 }
1267 }
1268
1269 // If there is already a loader task running, tell it to stop.
1270 // returns true if isLaunching() was true on the old task
1271 private boolean stopLoaderLocked() {
1272 boolean isLaunching = false;
1273 LoaderTask oldTask = mLoaderTask;
1274 if (oldTask != null) {
1275 if (oldTask.isLaunching()) {
1276 isLaunching = true;
1277 }
1278 oldTask.stopLocked();
1279 }
1280 return isLaunching;
1281 }
1282
1283 public boolean isCurrentCallbacks(Callbacks callbacks) {
1284 return (mCallbacks != null && mCallbacks.get() == callbacks);
1285 }
1286
1287 public void startLoader(boolean isLaunching, int synchronousBindPage) {
1288 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1289 }
1290
1291 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
1292 synchronized(mLock) {
1293 if (DEBUG_LOADERS) {
1294 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1295 }
1296 // Clear any deferred bind-runnables from the synchronized load process
1297 // We must do this before any loading/binding is scheduled below.
1298 synchronized(mDeferredBindRunnables) {
1299 mDeferredBindRunnables.clear();
1300 }
1301 // Don't bother to start the thread if we know it's not going to do anything
1302 if ((mCallbacks != null) && (mCallbacks.get() != null)) {
1303 // If there is already one running, tell it to stop.
1304 // also, don't downgrade isLaunching if we're already running
1305 isLaunching = isLaunching || stopLoaderLocked();
1306 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
1307 if (((synchronousBindPage != PagedView.INVALID_RESTORE_PAGE) && mAllAppsLoaded) && mWorks🔵
1308 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1309 } else {
1310 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1311 sWorker.post(mLoaderTask);
1312 }
1313 }
1314 }
1315 }
1316
1317 void bindRemainingSynchronousPages() {
1318 // Post the remaining side pages to be loaded
1319 if (!mDeferredBindRunnables.isEmpty()) {
1320 Runnable[] deferredBindRunnables = null;
1321 synchronized(mDeferredBindRunnables) {
1322 deferredBindRunnables = mDeferredBindRunnables.toArray(new Runnable[mDeferredBindRunnable🔵
1323 mDeferredBindRunnables.clear();
1324 }
1325 for (final Runnable r : deferredBindRunnables) {
1326 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
1327 }
1328 }
1329 }
1330
1331 public void stopLoader() {
1332 synchronized (mLock) {
1333 if (mLoaderTask != null) {
1334 mLoaderTask.stopLocked();
1335 }
1336 }
1337 }
1338
1339 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1340 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1341 final ContentResolver contentResolver = context.getContentResolver();
1342 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1343 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1344 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1345 try {
1346 final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID);
1347 final int rankIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens.SCREEN_RANK)🔵
1348 while (sc.moveToNext()) {
1349 try {
1350 long screenId = sc.getLong(idIndex);
1351 int rank = sc.getInt(rankIndex);
1352 orderedScreens.put(rank, screenId);
1353 } catch (java.lang.Exception e) {
1354 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e,🔵
1355 }
1356 }
1357 } finally {
1358 sc.close();
1359 }
1360 // Log to disk
1361 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1362 ArrayList<String> orderedScreensPairs = new ArrayList<String>();
1363 for (Integer i : orderedScreens.keySet()) {
1364 orderedScreensPairs.add(((("{ " + i) + ": ") + orderedScreens.get(i)) + " }");
1365 }
1366 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", orderedScreensPairs), tr🔵
1367 return orderedScreens;
1368 }
1369
1370 public boolean isAllAppsLoaded() {
1371 return mAllAppsLoaded;
1372 }
1373
1374 boolean isLoadingWorkspace() {
1375 synchronized (mLock) {
1376 if (mLoaderTask != null) {
1377 return mLoaderTask.isLoadingWorkspace();
1378 }
1379 }
1380 return false;
1381 }
1382
1383 /**
1384 * Runnable for the thread that loads the contents of the launcher:
1385 * - workspace icons
1386 * - widgets
1387 * - all apps icons
1388 */
1389 private class LoaderTask implements Runnable {
1390 private Context mContext;
1391
1392 private boolean mIsLaunching;
1393
1394 private boolean mIsLoadingAndBindingWorkspace;
1395
1396 private boolean mStopped;
1397
1398 private boolean mLoadAndBindStepFinished;
1399
1400 private int mFlags;
1401
1402 private HashMap<Object, CharSequence> mLabelCache;
1403
1404 LoaderTask(Context context, boolean isLaunching, int flags) {
1405 mContext = context;
1406 mIsLaunching = isLaunching;
1407 mLabelCache = new HashMap<Object, CharSequence>();
1408 mFlags = flags;
1409 }
1410
1411 boolean isLaunching() {
1412 return mIsLaunching;
1413 }
1414
1415 boolean isLoadingWorkspace() {
1416 return mIsLoadingAndBindingWorkspace;
1417 }
1418
1419 /** Returns whether this is an upgrade path */
1420 private boolean loadAndBindWorkspace() {
1421 mIsLoadingAndBindingWorkspace = true;
1422
1423 // Load the workspace
1424 if (DEBUG_LOADERS) {
1425 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
1426 }
1427
1428 boolean isUpgradePath = false;
1429 if (!mWorkspaceLoaded) {
1430 isUpgradePath = loadWorkspace();
1431 synchronized (LoaderTask.this) {
1432 if (mStopped) {
1433 return isUpgradePath;
1434 }
1435 mWorkspaceLoaded = true;
1436 }
1437 }
1438
1439 // Bind the workspace
1440 bindWorkspace(-1, isUpgradePath);
1441 return isUpgradePath;
1442 }
1443
1444 private void waitForIdle() {
1445 // Wait until the either we're stopped or the other threads are done.
1446 // This way we don't start loading all apps until the workspace has settled
1447 // down.
1448 synchronized (LoaderTask.this) {
1449 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1450
1451 mHandler.postIdle(new Runnable() {
1452 public void run() {
1453 synchronized (LoaderTask.this) {
1454 mLoadAndBindStepFinished = true;
1455 if (DEBUG_LOADERS) {
1456 Log.d(TAG, "done with previous binding step");
1457 }
1458 LoaderTask.this.notify();
1459 }
1460 }
1461 });
1462
1463 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
1464 try {
1465 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1466 // wait no longer than 1sec at a time
1467 this.wait(1000);
1468 } catch (InterruptedException ex) {
1469 // Ignore
1470 }
1471 }
1472 if (DEBUG_LOADERS) {
1473 Log.d(TAG, "waited "
1474 + (SystemClock.uptimeMillis()-workspaceWaitTime)
1475 + "ms for previous step to finish binding");
1476 }
1477 }
1478 }
1479
1480 void runBindSynchronousPage(int synchronousBindPage) {
1481 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
1482 // Ensure that we have a valid page index to load synchronously
1483 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1484 "valid page index");
1485 }
1486 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1487 // Ensure that we don't try and bind a specified page when the pages have not been
1488 // loaded already (we should load everything asynchronously in that case)
1489 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1490 }
1491 synchronized (mLock) {
1492 if (mIsLoaderTaskRunning) {
1493 // Ensure that we are never running the background loading at this point since
1494 // we also touch the background collections
1495 throw new RuntimeException("Error! Background loading is already running");
1496 }
1497 }
1498
1499 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1500 // data structures, we can't allow any other thread to touch that data, but because
1501 // this call is synchronous, we can get away with not locking).
1502
1503 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
1504 // operations from the previous activity. We need to ensure that all queued operations
1505 // are executed before any synchronous binding work is done.
1506 mHandler.flush();
1507
1508 // Divide the set of loaded items into those that we are binding synchronously, and
1509 // everything else that is to be bound normally (asynchronously).
1510 bindWorkspace(synchronousBindPage, false);
1511 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1512 // arise from that.
1513 onlyBindAllApps();
1514 }
1515
1516 public void run() {
1517 boolean isUpgrade = false;
1518 synchronized(mLock) {
1519 mIsLoaderTaskRunning = true;
1520 }
1521 // Optimize for end-user experience: if the Launcher is up and // running with the
1522 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1523 // workspace first (default).
1524 keep_running : {
1525 // Elevate priority when Home launches for the first time to avoid
1526 // starving at boot time. Staring at a blank home is not cool.
1527 synchronized(mLock) {
1528 if (DEBUG_LOADERS) {
1529 Log.d(TAG, "Setting thread priority to " + (mIsLaunching ? "DEFAULT" : "BACKGROUN🔵
1530 }
1531 Process.setThreadPriority(mIsLaunching ? java.lang.Process.THREAD_PRIORITY_DEFAULT : 🔵
1532 }
1533 if (DEBUG_LOADERS) {
1534 Log.d(TAG, "step 1: loading workspace");
1535 }
1536 isUpgrade = loadAndBindWorkspace();
1537 if (mStopped) {
1538 break keep_running;
1539 }
1540 // Whew! Hard work done. Slow us down, and wait until the UI thread has
1541 // settled down.
1542 synchronized(mLock) {
1543 if (mIsLaunching) {
1544 if (DEBUG_LOADERS) {
1545 Log.d(TAG, "Setting thread priority to BACKGROUND");
1546 }
1547 Process.setThreadPriority(java.lang.Process.THREAD_PRIORITY_BACKGROUND);
1548 }
1549 }
1550 waitForIdle();
1551 // second step
1552 if (DEBUG_LOADERS) {
1553 Log.d(TAG, "step 2: loading all apps");
1554 }
1555 loadAndBindAllApps();
1556 // Restore the default thread priority after we are done loading items
1557 synchronized(mLock) {
1558 Process.setThreadPriority(java.lang.Process.THREAD_PRIORITY_DEFAULT);
1559 }
1560 }
1561 // Update the saved icons if necessary
1562 if (DEBUG_LOADERS) {
1563 Log.d(TAG, "Comparing loaded icons to database icons");
1564 }
1565 synchronized(sBgLock) {
1566 for (Object key : sBgDbIconCache.keySet()) {
1567 updateSavedIcon(mContext, ((ShortcutInfo) (key)), sBgDbIconCache.get(key));
1568 }
1569 sBgDbIconCache.clear();
1570 }
1571 if (LauncherAppState.isDisableAllApps()) {
1572 // Ensure that all the applications that are in the system are
1573 // represented on the home screen.
1574 if ((!UPGRADE_USE_MORE_APPS_FOLDER) || (!isUpgrade)) {
1575 verifyApplications();
1576 }
1577 }
1578 // Clear out this reference, otherwise we end up holding it until all of the
1579 // callback runnables are done.
1580 mContext = null;
1581 synchronized(mLock) {
1582 // If we are still the last one to be scheduled, remove ourselves.
1583 if (mLoaderTask == this) {
1584 mLoaderTask = null;
1585 }
1586 mIsLoaderTaskRunning = false;
1587 }
1588 }
1589
1590 public void stopLocked() {
1591 synchronized (LoaderTask.this) {
1592 mStopped = true;
1593 this.notify();
1594 }
1595 }
1596
1597 /**
1598 * Gets the callbacks object. If we've been stopped, or if the launcher object
1599 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1600 * object that was around when the deferred message was scheduled, and if there's
1601 * a new Callbacks object around then also return null. This will save us from
1602 * calling onto it with data that will be ignored.
1603 */
1604 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1605 synchronized (mLock) {
1606 if (mStopped) {
1607 return null;
1608 }
1609
1610 if (mCallbacks == null) {
1611 return null;
1612 }
1613
1614 final Callbacks callbacks = mCallbacks.get();
1615 if (callbacks != oldCallbacks) {
1616 return null;
1617 }
1618 if (callbacks == null) {
1619 Log.w(TAG, "no mCallbacks");
1620 return null;
1621 }
1622
1623 return callbacks;
1624 }
1625 }
1626
1627 private void verifyApplications() {
1628 final Context context = mApp.getContext();
1629 // Cross reference all the applications in our apps list with items in the workspace
1630 ArrayList<ItemInfo> tmpInfos;
1631 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
1632 synchronized(sBgLock) {
1633 for (AppInfo app : mBgAllAppsList.data) {
1634 tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
1635 if (tmpInfos.isEmpty()) {
1636 // We are missing an application icon, so add this to the workspace
1637 added.add(app);
1638 // This is a rare event, so lets log it
1639 Log.e(TAG, "Missing Application on load: " + app);
1640 }
1641 }
1642 }
1643 if (!added.isEmpty()) {
1644 addAndBindAddedWorkspaceApps(context, added);
1645 }
1646 }
1647
1648 // check & update map of what's occupied; used to discard overlapping/invalid items
1649 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item, AtomicBoo🔵
1650 LauncherAppState app = LauncherAppState.getInstance();
1651 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1652 final int countX = ((int) (grid.numColumns));
1653 final int countY = ((int) (grid.numRows));
1654 long containerIndex = item.screenId;
1655 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1656 // Return early if we detect that an item is under the hotseat button
1657 if ((mCallbacks == null) || mCallbacks.get().isAllAppsButtonRank(((int) (item.screenId)))🔵
1658 deleteOnInvalidPlacement.set(true);
1659 Log.e(TAG, ((((((("Error loading shortcut into hotseat " + item) + " into position ("🔵
1660 return false;
1661 }
1662 final ItemInfo[][] hotseatItems = occupied.get(((long) (LauncherSettings.Favorites.CONTAI🔵
1663 if (item.screenId >= grid.numHotseatIcons) {
1664 Log.e(TAG, ((((("Error loading shortcut " + item) + " into hotseat position ") + item🔵
1665 return false;
1666 }
1667 if (hotseatItems != null) {
1668 if (hotseatItems[((int) (item.screenId))][0] != null) {
1669 Log.e(TAG, (((((((("Error loading shortcut into hotseat " + item) + " into positi🔵
1670 return false;
1671 } else {
1672 hotseatItems[((int) (item.screenId))][0] = item;
1673 return true;
1674 }
1675 } else {
1676 final ItemInfo[][] items = new ItemInfo[((int) (grid.numHotseatIcons))][1];
1677 items[((int) (item.screenId))][0] = item;
1678 occupied.put(((long) (LauncherSettings.Favorites.CONTAINER_HOTSEAT)), items);
1679 return true;
1680 }
1681 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1682 // Skip further checking if it is not the hotseat or workspace container
1683 return true;
1684 }
1685 if (!occupied.containsKey(item.screenId)) {
1686 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
1687 occupied.put(item.screenId, items);
1688 }
1689 final ItemInfo[][] screens = occupied.get(item.screenId);
1690 if (((((item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) && (item.cellX < 0)) 🔵
1691 Log.e(TAG, ((((((((((((("Error loading shortcut " + item) + " into cell (") + containerIn🔵
1692 return false;
1693 }
1694 // Check if any workspace icons overlap with each other
1695 for (int x = item.cellX; x < (item.cellX + item.spanX); x++) {
1696 for (int y = item.cellY; y < (item.cellY + item.spanY); y++) {
1697 if (screens[x][y] != null) {
1698 Log.e(TAG, (((((((((("Error loading shortcut " + item) + " into cell (") + contai🔵
1699 return false;
1700 }
1701 }
1702 }
1703 for (int x = item.cellX; x < (item.cellX + item.spanX); x++) {
1704 for (int y = item.cellY; y < (item.cellY + item.spanY); y++) {
1705 screens[x][y] = item;
1706 }
1707 }
1708 return true;
1709 }
1710
1711 /** Clears all the sBg data structures */
1712 private void clearSBgDataStructures() {
1713 synchronized (sBgLock) {
1714 sBgWorkspaceItems.clear();
1715 sBgAppWidgets.clear();
1716 sBgFolders.clear();
1717 sBgItemsIdMap.clear();
1718 sBgDbIconCache.clear();
1719 sBgWorkspaceScreens.clear();
1720 }
1721 }
1722
1723 /** Returns whether this is an upgrade path */
1724 private boolean loadWorkspace() {
1725 // Log to disk
1726 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
1727 final long t = (DEBUG_LOADERS) ? SystemClock.uptimeMillis() : 0;
1728 final Context context = mContext;
1729 final ContentResolver contentResolver = context.getContentResolver();
1730 final PackageManager manager = context.getPackageManager();
1731 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
1732 final boolean isSafeMode = manager.isSafeMode();
1733 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
1734 final boolean isSdCardReady = context.registerReceiver(null, new IntentFilter(StartupReceiver🔵
1735 LauncherAppState app = LauncherAppState.getInstance();
1736 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1737 int countX = ((int) (grid.numColumns));
1738 int countY = ((int) (grid.numRows));
1739 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
1740 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
1741 LauncherAppState.getLauncherProvider().deleteDatabase();
1742 }
1743 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1744 // append the user's Launcher2 shortcuts
1745 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
1746 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
1747 } else {
1748 // Make sure the default workspace is loaded
1749 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
1750 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
1751 }
1752 // This code path is for our old migration code and should no longer be exercised
1753 boolean loadedOldDb = false;
1754 // Log to disk
1755 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
1756 synchronized(sBgLock) {
1757 clearSBgDataStructures();
1758 final HashSet<String> installingPkgs = PackageInstallerCompat.getInstance(mContext).updat🔵
1759 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
1760 final ArrayList<Long> restoredRows = new ArrayList<Long>();
1761 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
1762 if (DEBUG_LOADERS) {
1763 Log.d(TAG, "loading model from " + contentUri);
1764 }
1765 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
1766 // +1 for the hotseat (it can be larger than the workspace)
1767 // Load workspace in reverse order to ensure that latest items are loaded first (and
1768 // before any earlier duplicates)
1769 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
1770 try {
1771 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1772 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1773 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1774 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYP🔵
1775 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1776 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_🔵
1777 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON🔵
1778 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAIN🔵
1779 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYP🔵
1780 final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWI🔵
1781 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites🔵
1782 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1783 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1784 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1785 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
1786 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
1787 final int restoredIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.RESTORED🔵
1788 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE🔵
1789 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1790 //final int displayModeIndex = c.getColumnIndexOrThrow(
1791 // LauncherSettings.Favorites.DISPLAY_MODE);
1792 ShortcutInfo info;
1793 String intentDescription;
1794 LauncherAppWidgetInfo appWidgetInfo;
1795 int container;
1796 long id;
1797 Intent intent;
1798 UserHandleCompat user;
1799 while ((!mStopped) && c.moveToNext()) {
1800 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
1801 try {
1802 int itemType = c.getInt(itemTypeIndex);
1803 boolean restored = 0 != c.getInt(restoredIndex);
1804 boolean allowMissingTarget = false;
1805 switch (itemType) {
1806 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION :
1807 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT :
1808 id = c.getLong(idIndex);
1809 intentDescription = c.getString(intentIndex);
1810 long serialNumber = c.getInt(profileIdIndex);
1811 user = mUserManager.getUserForSerialNumber(serialNumber);
1812 int promiseType = c.getInt(restoredIndex);
1813 if (user == null) {
1814 // User has been deleted remove the item.
1815 itemsToRemove.add(id);
1816 continue;
1817 }
1818 try {
1819 intent = Intent.parseUri(intentDescription, 0);
1820 ComponentName cn = intent.getComponent();
1821 if ((cn != null) && (cn.getPackageName() != null)) {
1822 boolean validPkg = launcherApps.isPackageEnabledForProfile(cn🔵
1823 boolean validComponent = validPkg && launcherApps.isActivityE🔵
1824 if (validComponent) {
1825 if (restored) {
1826 // no special handling necessary for this item
1827 restoredRows.add(id);
1828 restored = false;
1829 }
1830 } else if (validPkg) {
1831 intent = null;
1832 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 🔵
1833 // We allow auto install apps to have their intent
1834 // updated after an install.
1835 intent = manager.getLaunchIntentForPackage(cn.getPack🔵
1836 if (intent != null) {
1837 ContentValues values = new ContentValues();
1838 values.put(LauncherSettings.Favorites.INTENT, int🔵
1839 String where = BaseColumns._ID + "= ?";
1840 String[] args = new java.lang.String[]{ Long.toSt🔵
1841 contentResolver.update(contentUri, values, where,🔵
1842 }
1843 }
1844 if (intent == null) {
1845 // The app is installed but the component is no
1846 // longer available.
1847 Launcher.addDumpLog(TAG, "Invalid component removed: 🔵
1848 itemsToRemove.add(id);
1849 continue;
1850 } else {
1851 // no special handling necessary for this item
1852 restoredRows.add(id);
1853 restored = false;
1854 }
1855 } else if (restored) {
1856 // Package is not yet available but might be
1857 // installed later.
1858 Launcher.addDumpLog(TAG, "package not yet restored: " + c🔵
1859 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 🔵
1860 // Restore has started once.
1861 } else if (installingPkgs.contains(cn.getPackageName())) 🔵
1862 // App restore has started. Update the flag
1863 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
1864 ContentValues values = new ContentValues();
1865 values.put(LauncherSettings.Favorites.RESTORED, promi🔵
1866 String where = BaseColumns._ID + "= ?";
1867 String[] args = new java.lang.String[]{ Long.toString🔵
1868 contentResolver.update(contentUri, values, where, arg🔵
1869 } else if (REMOVE_UNRESTORED_ICONS) {
1870 Launcher.addDumpLog(TAG, "Unrestored package removed:🔵
1871 itemsToRemove.add(id);
1872 continue;
1873 }
1874 } else if (isSdCardReady) {
1875 // Do not wait for external media load anymore.
1876 // Log the invalid package, and remove it
1877 Launcher.addDumpLog(TAG, "Invalid package removed: " + cn🔵
1878 itemsToRemove.add(id);
1879 continue;
1880 } else {
1881 // SdCard is not ready yet. Package might get available,
1882 // once it is ready.
1883 Launcher.addDumpLog(TAG, ("Invalid package: " + cn) + " (🔵
1884 HashSet<String> pkgs = sPendingPackages.get(user);
1885 if (pkgs == null) {
1886 pkgs = new HashSet<String>();
1887 sPendingPackages.put(user, pkgs);
1888 }
1889 pkgs.add(cn.getPackageName());
1890 allowMissingTarget = true;
1891 // Add the icon on the workspace anyway.
1892 }
1893 } else if (cn == null) {
1894 // For shortcuts with no component, keep them as they are
1895 restoredRows.add(id);
1896 restored = false;
1897 }
1898 } catch (URISyntaxException e) {
1899 Launcher.addDumpLog(TAG, "Invalid uri: " + intentDescription, tru🔵
1900 continue;
1901 }
1902 if (restored) {
1903 if (user.equals(UserHandleCompat.myUserHandle())) {
1904 Launcher.addDumpLog(TAG, "constructing info for partially res🔵
1905 info = getRestoredItemInfo(c, titleIndex, intent, promiseType🔵
1906 intent = getRestoredItemIntent(c, context, intent);
1907 } else {
1908 // Don't restore items for other profiles.
1909 itemsToRemove.add(id);
1910 continue;
1911 }
1912 } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATI🔵
1913 info = getShortcutInfo(manager, intent, user, context, c, iconInd🔵
1914 } else {
1915 info = getShortcutInfo(c, context, iconTypeIndex, iconPackageInde🔵
1916 // App shortcuts that used to be automatically added to Launcher
1917 // didn't always have the correct intent flags set, so do that
1918 // here
1919 if ((((intent.getAction() != null) && (intent.getCategories() != 🔵
1920 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_A🔵
1921 }
1922 }
1923 if (info != null) {
1924 info.id = id;
1925 info.intent = intent;
1926 container = c.getInt(containerIndex);
1927 info.container = container;
1928 info.screenId = c.getInt(screenIndex);
1929 info.cellX = c.getInt(cellXIndex);
1930 info.cellY = c.getInt(cellYIndex);
1931 info.spanX = 1;
1932 info.spanY = 1;
1933 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
1934 info.isDisabled = isSafeMode && (!Utilities.isSystemApp(context, 🔵
1935 // check & update map of what's occupied
1936 deleteOnInvalidPlacement.set(false);
1937 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)🔵
1938 if (deleteOnInvalidPlacement.get()) {
1939 itemsToRemove.add(id);
1940 }
1941 break;
1942 }
1943 switch (container) {
1944 case LauncherSettings.Favorites.CONTAINER_DESKTOP :
1945 case LauncherSettings.Favorites.CONTAINER_HOTSEAT :
1946 sBgWorkspaceItems.add(info);
1947 break;
1948 default :
1949 // Item is in a user folder
1950 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, cont🔵
1951 folderInfo.add(info);
1952 break;
1953 }
1954 sBgItemsIdMap.put(info.id, info);
1955 // now that we've loaded everthing re-save it with the
1956 // icon in case it disappears somehow.
1957 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
1958 } else {
1959 throw new RuntimeException("Unexpected null ShortcutInfo");
1960 }
1961 break;
1962 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER :
1963 id = c.getLong(idIndex);
1964 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
1965 folderInfo.title = c.getString(titleIndex);
1966 folderInfo.id = id;
1967 container = c.getInt(containerIndex);
1968 folderInfo.container = container;
1969 folderInfo.screenId = c.getInt(screenIndex);
1970 folderInfo.cellX = c.getInt(cellXIndex);
1971 folderInfo.cellY = c.getInt(cellYIndex);
1972 folderInfo.spanX = 1;
1973 folderInfo.spanY = 1;
1974 // check & update map of what's occupied
1975 deleteOnInvalidPlacement.set(false);
1976 if (!checkItemPlacement(occupied, folderInfo, deleteOnInvalidPlacemen🔵
1977 if (deleteOnInvalidPlacement.get()) {
1978 itemsToRemove.add(id);
1979 }
1980 break;
1981 }
1982 switch (container) {
1983 case LauncherSettings.Favorites.CONTAINER_DESKTOP :
1984 case LauncherSettings.Favorites.CONTAINER_HOTSEAT :
1985 sBgWorkspaceItems.add(folderInfo);
1986 break;
1987 }
1988 if (restored) {
1989 // no special handling required for restored folders
1990 restoredRows.add(id);
1991 }
1992 sBgItemsIdMap.put(folderInfo.id, folderInfo);
1993 sBgFolders.put(folderInfo.id, folderInfo);
1994 break;
1995 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET :
1996 // Read all Launcher-specific widget details
1997 int appWidgetId = c.getInt(appWidgetIdIndex);
1998 String savedProvider = c.getString(appWidgetProviderIndex);
1999 id = c.getLong(idIndex);
2000 final ComponentName component = ComponentName.unflattenFromString(sav🔵
2001 final int restoreStatus = c.getInt(restoredIndex);
2002 final boolean isIdValid = (restoreStatus & LauncherAppWidgetInfo.FLAG🔵
2003 final boolean wasProviderReady = (restoreStatus & LauncherAppWidgetIn🔵
2004 final AppWidgetProviderInfo provider = (isIdValid) ? widgets.getAppWi🔵
2005 final boolean isProviderReady = isValidProvider(provider);
2006 if (((!isSafeMode) && wasProviderReady) && (!isProviderReady)) {
2007 String log = ((("Deleting widget that isn\'t installed anymore: "🔵
2008 Log.e(TAG, log);
2009 Launcher.addDumpLog(TAG, log, false);
2010 itemsToRemove.add(id);
2011 } else {
2012 if (isProviderReady) {
2013 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, provid🔵
2014 int[] minSpan = Launcher.getMinSpanForWidget(context, provide🔵
2015 appWidgetInfo.minSpanX = minSpan[0];
2016 appWidgetInfo.minSpanY = minSpan[1];
2017 int status = restoreStatus;
2018 if (!wasProviderReady) {
2019 // If provider was not previously ready, update the
2020 // status and UI flag.
2021 // Id would be valid only if the widget restore broadcast🔵
2022 if (isIdValid) {
2023 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
2024 } else {
2025 status &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_RE🔵
2026 }
2027 }
2028 appWidgetInfo.restoreStatus = status;
2029 } else {
2030 Log.v(TAG, (((("Widget restore pending id=" + id) + " appWidg🔵
2031 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, compon🔵
2032 appWidgetInfo.restoreStatus = restoreStatus;
2033 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_START🔵
2034 // Restore has started once.
2035 } else if (installingPkgs.contains(component.getPackageName()🔵
2036 // App restore has started. Update the flag
2037 appWidgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG🔵
2038 } else if (REMOVE_UNRESTORED_ICONS) {
2039 Launcher.addDumpLog(TAG, "Unrestored widget removed: " + 🔵
2040 itemsToRemove.add(id);
2041 continue;
2042 }
2043 }
2044 appWidgetInfo.id = id;
2045 appWidgetInfo.screenId = c.getInt(screenIndex);
2046 appWidgetInfo.cellX = c.getInt(cellXIndex);
2047 appWidgetInfo.cellY = c.getInt(cellYIndex);
2048 appWidgetInfo.spanX = c.getInt(spanXIndex);
2049 appWidgetInfo.spanY = c.getInt(spanYIndex);
2050 container = c.getInt(containerIndex);
2051 if ((container != LauncherSettings.Favorites.CONTAINER_DESKTOP) &🔵
2052 Log.e(TAG, "Widget found where container != " + "CONTAINER_DE🔵
2053 continue;
2054 }
2055 appWidgetInfo.container = c.getInt(containerIndex);
2056 // check & update map of what's occupied
2057 deleteOnInvalidPlacement.set(false);
2058 if (!checkItemPlacement(occupied, appWidgetInfo, deleteOnInvalidP🔵
2059 if (deleteOnInvalidPlacement.get()) {
2060 itemsToRemove.add(id);
2061 }
2062 break;
2063 }
2064 String providerName = appWidgetInfo.providerName.flattenToString(🔵
2065 if ((!providerName.equals(savedProvider)) || (appWidgetInfo.resto🔵
2066 ContentValues values = new ContentValues();
2067 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, pro🔵
2068 values.put(LauncherSettings.Favorites.RESTORED, appWidgetInfo🔵
2069 String where = BaseColumns._ID + "= ?";
2070 String[] args = new java.lang.String[]{ Long.toString(id) };
2071 contentResolver.update(contentUri, values, where, args);
2072 }
2073 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2074 sBgAppWidgets.add(appWidgetInfo);
2075 }
2076 break;
2077 }
2078 } catch (java.lang.Exception e) {
2079 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
2080 }
2081 }
2082 } finally {
2083 if (c != null) {
2084 c.close();
2085 }
2086 }
2087 // Break early if we've stopped loading
2088 if (mStopped) {
2089 clearSBgDataStructures();
2090 return false;
2091 }
2092 if (itemsToRemove.size() > 0) {
2093 ContentProviderClient client = contentResolver.acquireContentProviderClient(contentUr🔵
2094 // Remove dead items
2095 for (long id : itemsToRemove) {
2096 if (DEBUG_LOADERS) {
2097 Log.d(TAG, "Removed id = " + id);
2098 }
2099 // Don't notify content observers
2100 try {
2101 client.delete(LauncherSettings.Favorites.getContentUri(id, false), null, null🔵
2102 } catch (RemoteException e) {
2103 Log.w(TAG, "Could not remove id = " + id);
2104 }
2105 }
2106 }
2107 if (restoredRows.size() > 0) {
2108 ContentProviderClient updater = contentResolver.acquireContentProviderClient(contentU🔵
2109 // Update restored items that no longer require special handling
2110 try {
2111 StringBuilder selectionBuilder = new StringBuilder();
2112 selectionBuilder.append(LauncherSettings.Favorites._ID);
2113 selectionBuilder.append(" IN (");
2114 selectionBuilder.append(TextUtils.join(", ", restoredRows));
2115 selectionBuilder.append(")");
2116 ContentValues values = new ContentValues();
2117 values.put(LauncherSettings.Favorites.RESTORED, 0);
2118 updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values, se🔵
2119 } catch (RemoteException e) {
2120 Log.w(TAG, "Could not update restored rows");
2121 }
2122 }
2123 if ((!isSdCardReady) && (!sPendingPackages.isEmpty())) {
2124 context.registerReceiver(new AppsAvailabilityCheck(), new IntentFilter(StartupReceive🔵
2125 }
2126 if (loadedOldDb) {
2127 long maxScreenId = 0;
2128 // If we're importing we use the old screen order.
2129 for (ItemInfo item : sBgItemsIdMap.values()) {
2130 long screenId = item.screenId;
2131 if ((item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) && (!sBgWork🔵
2132 sBgWorkspaceScreens.add(screenId);
2133 if (screenId > maxScreenId) {
2134 maxScreenId = screenId;
2135 }
2136 }
2137 }
2138 Collections.sort(sBgWorkspaceScreens);
2139 // Log to disk
2140 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
2141 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + TextUtils.join(", ", 🔵
2142 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
2143 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2144 // Update the max item id after we load an old db
2145 long maxItemId = 0;
2146 // If we're importing we use the old screen order.
2147 for (ItemInfo item : sBgItemsIdMap.values()) {
2148 maxItemId = Math.max(maxItemId, item.id);
2149 }
2150 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
2151 } else {
2152 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
2153 for (Integer i : orderedScreens.keySet()) {
2154 sBgWorkspaceScreens.add(orderedScreens.get(i));
2155 }
2156 // Log to disk
2157 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + TextUtils.join(", ", 🔵
2158 // Remove any empty screens
2159 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
2160 for (ItemInfo item : sBgItemsIdMap.values()) {
2161 long screenId = item.screenId;
2162 if ((item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) && unusedScr🔵
2163 unusedScreens.remove(screenId);
2164 }
2165 }
2166 // If there are any empty screens remove them, and update.
2167 if (unusedScreens.size() != 0) {
2168 // Log to disk
2169 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " + TextUti🔵
2170 sBgWorkspaceScreens.removeAll(unusedScreens);
2171 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2172 }
2173 }
2174 if (DEBUG_LOADERS) {
2175 Log.d(TAG, ("loaded workspace in " + (SystemClock.uptimeMillis() - t)) + "ms");
2176 Log.d(TAG, "workspace layout: ");
2177 int nScreens = occupied.size();
2178 for (int y = 0; y < countY; y++) {
2179 String line = "";
2180 Iterator<Long> iter = occupied.keySet().iterator();
2181 while (iter.hasNext()) {
2182 long screenId = iter.next();
2183 if (screenId > 0) {
2184 line += " | ";
2185 }
2186 for (int x = 0; x < countX; x++) {
2187 ItemInfo[][] screen = occupied.get(screenId);
2188 if ((x < screen.length) && (y < screen[x].length)) {
2189 line += (screen[x][y] != null) ? "#" : ".";
2190 } else {
2191 line += "!";
2192 }
2193 }
2194 }
2195 Log.d(TAG, ("[ " + line) + " ]");
2196 }
2197 }
2198 }
2199 return loadedOldDb;
2200 }
2201
2202 /** Filters the set of items who are directly or indirectly (via another container) on the
2203 * specified screen. */
2204 private void filterCurrentWorkspaceItems(long currentScreenId, ArrayList<ItemInfo> allWorkspaceIt🔵
2205 // Purge any null ItemInfos
2206 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2207 while (iter.hasNext()) {
2208 ItemInfo i = iter.next();
2209 if (i == null) {
2210 iter.remove();
2211 }
2212 }
2213 // Order the set of items by their containers first, this allows use to walk through the
2214 // list sequentially, build up a list of containers that are in the specified screen,
2215 // as well as all items in those containers.
2216 Set<Long> itemsOnScreen = new HashSet<Long>();
2217 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2218 @Override
2219 public int compare(ItemInfo lhs, ItemInfo rhs) {
2220 return ((int) (lhs.container - rhs.container));
2221 }
2222 });
2223 for (ItemInfo info : allWorkspaceItems) {
2224 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
2225 if (info.screenId == currentScreenId) {
2226 currentScreenItems.add(info);
2227 itemsOnScreen.add(info.id);
2228 } else {
2229 otherScreenItems.add(info);
2230 }
2231 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2232 currentScreenItems.add(info);
2233 itemsOnScreen.add(info.id);
2234 } else if (itemsOnScreen.contains(info.container)) {
2235 currentScreenItems.add(info);
2236 itemsOnScreen.add(info.id);
2237 } else {
2238 otherScreenItems.add(info);
2239 }
2240 }
2241 }
2242
2243 /** Filters the set of widgets which are on the specified screen. */
2244 private void filterCurrentAppWidgets(long currentScreenId, ArrayList<LauncherAppWidgetInfo> appWi🔵
2245 for (LauncherAppWidgetInfo widget : appWidgets) {
2246 if (widget == null) {
2247 continue;
2248 }
2249 if ((widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) && (widget.screenI🔵
2250 currentScreenWidgets.add(widget);
2251 } else {
2252 otherScreenWidgets.add(widget);
2253 }
2254 }
2255 }
2256
2257 /** Filters the set of folders which are on the specified screen. */
2258 private void filterCurrentFolders(long currentScreenId, HashMap<Long, ItemInfo> itemsIdMap, HashM🔵
2259 for (long id : folders.keySet()) {
2260 ItemInfo info = itemsIdMap.get(id);
2261 FolderInfo folder = folders.get(id);
2262 if ((info == null) || (folder == null)) {
2263 continue;
2264 }
2265 if ((info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) && (info.screenId ==🔵
2266 currentScreenFolders.put(id, folder);
2267 } else {
2268 otherScreenFolders.put(id, folder);
2269 }
2270 }
2271 }
2272
2273 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2274 * right) */
2275 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
2276 final LauncherAppState app = LauncherAppState.getInstance();
2277 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2278 // XXX: review this
2279 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
2280 @Override
2281 public int compare(ItemInfo lhs, ItemInfo rhs) {
2282 int cellCountX = ((int) (grid.numColumns));
2283 int cellCountY = ((int) (grid.numRows));
2284 int screenOffset = cellCountX * cellCountY;
2285 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1);// +1 hotseat
2286
2287 long lr = (((lhs.container * containerOffset) + (lhs.screenId * screenOffset)) + (lhs🔵
2288 long rr = (((rhs.container * containerOffset) + (rhs.screenId * screenOffset)) + (rhs🔵
2289 return ((int) (lr - rr));
2290 }
2291 });
2292 }
2293
2294 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2295 final ArrayList<Long> orderedScreens) {
2296 final Runnable r = new Runnable() {
2297 @Override
2298 public void run() {
2299 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2300 if (callbacks != null) {
2301 callbacks.bindScreens(orderedScreens);
2302 }
2303 }
2304 };
2305 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2306 }
2307
2308 private void bindWorkspaceItems(final Callbacks oldCallbacks, final ArrayList<ItemInfo> workspace🔵
2309 final boolean postOnMainThread = deferredBindRunnables != null;
2310 // Bind the workspace items
2311 int N = workspaceItems.size();
2312 for (int i = 0; i < N; i += ITEMS_CHUNK) {
2313 final int start = i;
2314 final int chunkSize = ((i + ITEMS_CHUNK) <= N) ? ITEMS_CHUNK : N - i;
2315 final Runnable r = new Runnable() {
2316 @Override
2317 public void run() {
2318 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2319 if (callbacks != null) {
2320 callbacks.bindItems(workspaceItems, start, start + chunkSize, false);
2321 }
2322 }
2323 };
2324 if (postOnMainThread) {
2325 synchronized(deferredBindRunnables) {
2326 deferredBindRunnables.add(r);
2327 }
2328 } else {
2329 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2330 }
2331 }
2332 // Bind the folders
2333 if (!folders.isEmpty()) {
2334 final Runnable r = new Runnable() {
2335 public void run() {
2336 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2337 if (callbacks != null) {
2338 callbacks.bindFolders(folders);
2339 }
2340 }
2341 };
2342 if (postOnMainThread) {
2343 synchronized(deferredBindRunnables) {
2344 deferredBindRunnables.add(r);
2345 }
2346 } else {
2347 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2348 }
2349 }
2350 // Bind the widgets, one at a time
2351 N = appWidgets.size();
2352 for (int i = 0; i < N; i++) {
2353 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2354 final Runnable r = new Runnable() {
2355 public void run() {
2356 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2357 if (callbacks != null) {
2358 callbacks.bindAppWidget(widget);
2359 }
2360 }
2361 };
2362 if (postOnMainThread) {
2363 deferredBindRunnables.add(r);
2364 } else {
2365 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2366 }
2367 }
2368 }
2369
2370 /**
2371 * Binds all loaded data to actual views on the main thread.
2372 */
2373 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
2374 final long t = SystemClock.uptimeMillis();
2375 Runnable r;
2376 // Don't use these two variables in any of the callback runnables.
2377 // Otherwise we hold a reference to them.
2378 final Callbacks oldCallbacks = mCallbacks.get();
2379 if (oldCallbacks == null) {
2380 // This launcher has exited and nobody bothered to tell us. Just bail.
2381 Log.w(TAG, "LoaderTask running with no launcher");
2382 return;
2383 }
2384 // Save a copy of all the bg-thread collections
2385 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
2386 ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<LauncherAppWidgetInfo>();
2387 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
2388 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
2389 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
2390 synchronized(sBgLock) {
2391 workspaceItems.addAll(sBgWorkspaceItems);
2392 appWidgets.addAll(sBgAppWidgets);
2393 folders.putAll(sBgFolders);
2394 itemsIdMap.putAll(sBgItemsIdMap);
2395 orderedScreenIds.addAll(sBgWorkspaceScreens);
2396 }
2397 final boolean isLoadingSynchronously = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
2398 int currScreen = (isLoadingSynchronously) ? synchronizeBindPage : oldCallbacks.getCurrentWork🔵
2399 if (currScreen >= orderedScreenIds.size()) {
2400 // There may be no workspace screens (just hotseat items and an empty page).
2401 currScreen = PagedView.INVALID_RESTORE_PAGE;
2402 }
2403 final int currentScreen = currScreen;
2404 final long currentScreenId = (currentScreen < 0) ? INVALID_SCREEN_ID : orderedScreenIds.get(c🔵
2405 // Load all the items that are on the current page first (and in the process, unbind
2406 // all the existing workspace items before we call startBinding() below.
2407 unbindWorkspaceItemsOnMainThread();
2408 // Separate the items that are on the current screen, and all the other remaining items
2409 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
2410 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
2411 ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
2412 ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
2413 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
2414 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
2415 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWork🔵
2416 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets, otherAppWidgets);
2417 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders, otherFolders);
2418 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2419 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2420 // Tell the workspace that we're about to start binding items
2421 r = new Runnable() {
2422 public void run() {
2423 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2424 if (callbacks != null) {
2425 callbacks.startBinding();
2426 }
2427 }
2428 };
2429 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2430 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2431 // Load items on the current page
2432 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, nu🔵
2433 if (isLoadingSynchronously) {
2434 r = new Runnable() {
2435 public void run() {
2436 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2437 if ((callbacks != null) && (currentScreen != PagedView.INVALID_RESTORE_PAGE)) {
2438 callbacks.onPageBoundSynchronously(currentScreen);
2439 }
2440 }
2441 };
2442 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2443 }
2444 // Load all the remaining pages (if we are loading synchronously, we want to defer this
2445 // work until after the first render)
2446 synchronized(mDeferredBindRunnables) {
2447 mDeferredBindRunnables.clear();
2448 }
2449 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, isLoadin🔵
2450 // Tell the workspace that we're done binding items
2451 r = new Runnable() {
2452 public void run() {
2453 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2454 if (callbacks != null) {
2455 callbacks.finishBindingItems(isUpgradePath);
2456 }
2457 // If we're profiling, ensure this is the last thing in the queue.
2458 if (DEBUG_LOADERS) {
2459 Log.d(TAG, ("bound workspace in " + (SystemClock.uptimeMillis() - t)) + "ms");
2460 }
2461 mIsLoadingAndBindingWorkspace = false;
2462 }
2463 };
2464 if (isLoadingSynchronously) {
2465 synchronized(mDeferredBindRunnables) {
2466 mDeferredBindRunnables.add(r);
2467 }
2468 } else {
2469 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2470 }
2471 }
2472
2473 private void loadAndBindAllApps() {
2474 if (DEBUG_LOADERS) {
2475 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2476 }
2477 if (!mAllAppsLoaded) {
2478 loadAllApps();
2479 synchronized (LoaderTask.this) {
2480 if (mStopped) {
2481 return;
2482 }
2483 mAllAppsLoaded = true;
2484 }
2485 } else {
2486 onlyBindAllApps();
2487 }
2488 }
2489
2490 private void onlyBindAllApps() {
2491 final Callbacks oldCallbacks = mCallbacks.get();
2492 if (oldCallbacks == null) {
2493 // This launcher has exited and nobody bothered to tell us. Just bail.
2494 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2495 return;
2496 }
2497 // shallow copy
2498 @SuppressWarnings("unchecked")
2499 final ArrayList<AppInfo> list = ((ArrayList<AppInfo>) (mBgAllAppsList.data.clone()));
2500 Runnable r = new Runnable() {
2501 public void run() {
2502 final long t = SystemClock.uptimeMillis();
2503 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2504 if (callbacks != null) {
2505 callbacks.bindAllApplications(list);
2506 }
2507 if (DEBUG_LOADERS) {
2508 Log.d(TAG, ((("bound all " + list.size()) + " apps from cache in ") + (SystemCloc🔵
2509 }
2510 }
2511 };
2512 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
2513 if (isRunningOnMainThread) {
2514 r.run();
2515 } else {
2516 mHandler.post(r);
2517 }
2518 }
2519
2520 private void loadAllApps() {
2521 final long loadTime = (DEBUG_LOADERS) ? SystemClock.uptimeMillis() : 0;
2522 final Callbacks oldCallbacks = mCallbacks.get();
2523 if (oldCallbacks == null) {
2524 // This launcher has exited and nobody bothered to tell us. Just bail.
2525 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
2526 return;
2527 }
2528 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
2529 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2530 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
2531 // Clear the list of apps
2532 mBgAllAppsList.clear();
2533 for (UserHandleCompat user : profiles) {
2534 // Query for the set of apps
2535 final long qiaTime = (DEBUG_LOADERS) ? SystemClock.uptimeMillis() : 0;
2536 List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
2537 if (DEBUG_LOADERS) {
2538 Log.d(TAG, (("getActivityList took " + (SystemClock.uptimeMillis() - qiaTime)) + "ms 🔵
2539 Log.d(TAG, (("getActivityList got " + apps.size()) + " apps for user ") + user);
2540 }
2541 // Fail if we don't have any apps
2542 if ((apps == null) || apps.isEmpty()) {
2543 return;
2544 }
2545 // Sort the applications by name
2546 final long sortTime = (DEBUG_LOADERS) ? SystemClock.uptimeMillis() : 0;
2547 Collections.sort(apps, new LauncherModel.ShortcutNameComparator(mLabelCache));
2548 if (DEBUG_LOADERS) {
2549 Log.d(TAG, ("sort took " + (SystemClock.uptimeMillis() - sortTime)) + "ms");
2550 }
2551 // Create the ApplicationInfos
2552 for (int i = 0; i < apps.size(); i++) {
2553 LauncherActivityInfoCompat app = apps.get(i);
2554 // This builds the icon bitmaps.
2555 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
2556 }
2557 }
2558 // Huh? Shouldn't this be inside the Runnable below?
2559 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2560 mBgAllAppsList.added = new ArrayList<AppInfo>();
2561 // Post callback on main thread
2562 mHandler.post(new Runnable() {
2563 public void run() {
2564 final long bindTime = SystemClock.uptimeMillis();
2565 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2566 if (callbacks != null) {
2567 callbacks.bindAllApplications(added);
2568 if (DEBUG_LOADERS) {
2569 Log.d(TAG, ((("bound " + added.size()) + " apps in ") + (SystemClock.uptimeMi🔵
2570 }
2571 } else {
2572 Log.i(TAG, "not binding apps: no Launcher activity");
2573 }
2574 }
2575 });
2576 if (DEBUG_LOADERS) {
2577 Log.d(TAG, ("Icons processed in " + (SystemClock.uptimeMillis() - loadTime)) + "ms");
2578 }
2579 }
2580
2581 public void dumpState() {
2582 synchronized(sBgLock) {
2583 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
2584 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
2585 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2586 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2587 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2588 }
2589 }
2590 }
2591
2592 void enqueuePackageUpdated(PackageUpdatedTask task) {
2593 sWorker.post(task);
2594 }
2595
2596 private class AppsAvailabilityCheck extends BroadcastReceiver {
2597 @Override
2598 public void onReceive(Context context, Intent intent) {
2599 synchronized(sBgLock) {
2600 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mApp.getContext())🔵
2601 ArrayList<String> packagesRemoved;
2602 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
2603 UserHandleCompat user = entry.getKey();
2604 packagesRemoved = new ArrayList<String>();
2605 for (String pkg : entry.getValue()) {
2606 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
2607 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
2608 packagesRemoved.add(pkg);
2609 }
2610 }
2611 if (!packagesRemoved.isEmpty()) {
2612 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE, packag🔵
2613 }
2614 }
2615 sPendingPackages.clear();
2616 }
2617 }
2618 }
2619
2620 /**
2621 * Workaround to re-check unrestored items, in-case they were installed but the Package-ADD
2622 * runnable was missed by the launcher.
2623 */
2624 public void recheckRestoredItems(final Context context) {
2625 Runnable r = new Runnable() {
2626 @Override
2627 public void run() {
2628 LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2629 HashSet<String> installedPackages = new HashSet<String>();
2630 UserHandleCompat user = UserHandleCompat.myUserHandle();
2631 synchronized(sBgLock) {
2632 for (ItemInfo info : sBgItemsIdMap.values()) {
2633 if (info instanceof ShortcutInfo) {
2634 ShortcutInfo si = ((ShortcutInfo) (info));
2635 if ((si.isPromise() && (si.getTargetComponent() != null)) && launcherApps.isP🔵
2636 installedPackages.add(si.getTargetComponent().getPackageName());
2637 }
2638 } else if (info instanceof LauncherAppWidgetInfo) {
2639 LauncherAppWidgetInfo widget = ((LauncherAppWidgetInfo) (info));
2640 if (widget.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && l🔵
2641 installedPackages.add(widget.providerName.getPackageName());
2642 }
2643 }
2644 }
2645 }
2646 if (!installedPackages.isEmpty()) {
2647 final ArrayList<AppInfo> restoredApps = new ArrayList<AppInfo>();
2648 for (String pkg : installedPackages) {
2649 for (LauncherActivityInfoCompat info : launcherApps.getActivityList(pkg, user)) {
2650 restoredApps.add(new AppInfo(context, info, user, mIconCache, null));
2651 }
2652 }
2653 final Callbacks callbacks = (mCallbacks != null) ? mCallbacks.get() : null;
2654 if (!restoredApps.isEmpty()) {
2655 mHandler.post(new Runnable() {
2656 public void run() {
2657 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
2658 if ((callbacks == cb) && (cb != null)) {
2659 callbacks.bindAppsRestored(restoredApps);
2660 }
2661 }
2662 });
2663 }
2664 }
2665 }
2666 };
2667 sWorker.post(r);
2668 }
2669
2670 private class PackageUpdatedTask implements Runnable {
2671 int mOp;
2672
2673 String[] mPackages;
2674
2675 UserHandleCompat mUser;
2676
2677 public static final int OP_NONE = 0;
2678
2679 public static final int OP_ADD = 1;
2680
2681 public static final int OP_UPDATE = 2;
2682
2683 // uninstlled
2684 public static final int OP_REMOVE = 3; // uninstlled
2685
2686 // external media unmounted
2687 public static final int OP_UNAVAILABLE = 4; // external media unmounted
2688
2689 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
2690 mOp = op;
2691 mPackages = packages;
2692 mUser = user;
2693 }
2694
2695 public void run() {
2696 final Context context = mApp.getContext();
2697 final String[] packages = mPackages;
2698 final int N = packages.length;
2699 switch (mOp) {
2700 case OP_ADD :
2701 for (int i = 0; i < N; i++) {
2702 if (DEBUG_LOADERS) {
2703 Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
2704 }
2705 mIconCache.remove(packages[i], mUser);
2706 mBgAllAppsList.addPackage(context, packages[i], mUser);
2707 }
2708 break;
2709 case OP_UPDATE :
2710 for (int i = 0; i < N; i++) {
2711 if (DEBUG_LOADERS) {
2712 Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
2713 }
2714 mBgAllAppsList.updatePackage(context, packages[i], mUser);
2715 WidgetPreviewLoader.removePackageFromDb(mApp.getWidgetPreviewCacheDb(), packages[🔵
2716 }
2717 break;
2718 case OP_REMOVE :
2719 case OP_UNAVAILABLE :
2720 for (int i = 0; i < N; i++) {
2721 if (DEBUG_LOADERS) {
2722 Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
2723 }
2724 mBgAllAppsList.removePackage(packages[i], mUser);
2725 WidgetPreviewLoader.removePackageFromDb(mApp.getWidgetPreviewCacheDb(), packages[🔵
2726 }
2727 break;
2728 }
2729 ArrayList<AppInfo> added = null;
2730 ArrayList<AppInfo> modified = null;
2731 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
2732 if (mBgAllAppsList.added.size() > 0) {
2733 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
2734 mBgAllAppsList.added.clear();
2735 }
2736 if (mBgAllAppsList.modified.size() > 0) {
2737 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
2738 mBgAllAppsList.modified.clear();
2739 }
2740 if (mBgAllAppsList.removed.size() > 0) {
2741 removedApps.addAll(mBgAllAppsList.removed);
2742 mBgAllAppsList.removed.clear();
2743 }
2744 final Callbacks callbacks = (mCallbacks != null) ? mCallbacks.get() : null;
2745 if (callbacks == null) {
2746 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
2747 return;
2748 }
2749 if (added != null) {
2750 // Ensure that we add all the workspace applications to the db
2751 if (LauncherAppState.isDisableAllApps()) {
2752 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
2753 addAndBindAddedWorkspaceApps(context, addedInfos);
2754 } else {
2755 addAppsToAllApps(context, added);
2756 }
2757 }
2758 if (modified != null) {
2759 final ArrayList<AppInfo> modifiedFinal = modified;
2760 // Update the launcher db to reflect the changes
2761 for (AppInfo a : modifiedFinal) {
2762 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
2763 for (ItemInfo i : infos) {
2764 if (isShortcutInfoUpdateable(i)) {
2765 ShortcutInfo info = ((ShortcutInfo) (i));
2766 info.title = a.title.toString();
2767 info.contentDescription = a.contentDescription;
2768 updateItemInDatabase(context, info);
2769 }
2770 }
2771 }
2772 mHandler.post(new Runnable() {
2773 public void run() {
2774 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
2775 if ((callbacks == cb) && (cb != null)) {
2776 callbacks.bindAppsUpdated(modifiedFinal);
2777 }
2778 }
2779 });
2780 }
2781 final ArrayList<String> removedPackageNames = new ArrayList<String>();
2782 if (mOp == OP_REMOVE) {
2783 // Mark all packages in the broadcast to be removed
2784 removedPackageNames.addAll(Arrays.asList(packages));
2785 } else if (mOp == OP_UPDATE) {
2786 // Mark disabled packages in the broadcast to be removed
2787 final PackageManager pm = context.getPackageManager();
2788 for (int i = 0; i < N; i++) {
2789 if (isPackageDisabled(context, packages[i], mUser)) {
2790 removedPackageNames.add(packages[i]);
2791 }
2792 }
2793 }
2794 // Remove all the components associated with this package
2795 for (String pn : removedPackageNames) {
2796 deletePackageFromDatabase(context, pn, mUser);
2797 }
2798 // Remove all the specific components
2799 for (AppInfo a : removedApps) {
2800 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
2801 deleteItemsFromDatabase(context, infos);
2802 }
2803 if ((!removedPackageNames.isEmpty()) || (!removedApps.isEmpty())) {
2804 // Remove any queued items from the install queue
2805 String spKey = LauncherAppState.getSharedPreferencesKey();
2806 SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
2807 InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
2808 // Call the components-removed callback
2809 mHandler.post(new Runnable() {
2810 public void run() {
2811 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
2812 if ((callbacks == cb) && (cb != null)) {
2813 callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
2814 }
2815 }
2816 });
2817 }
2818 final ArrayList<Object> widgetsAndShortcuts = getSortedWidgetsAndShortcuts(context);
2819 mHandler.post(new Runnable() {
2820 @Override
2821 public void run() {
2822 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
2823 if ((callbacks == cb) && (cb != null)) {
2824 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
2825 }
2826 }
2827 });
2828 // Write all the logs to disk
2829 mHandler.post(new Runnable() {
2830 public void run() {
2831 Callbacks cb = (mCallbacks != null) ? mCallbacks.get() : null;
2832 if ((callbacks == cb) && (cb != null)) {
2833 callbacks.dumpLogsToLocalData();
2834 }
2835 }
2836 });
2837 }
2838 }
2839
2840 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
2841 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
2842 PackageManager packageManager = context.getPackageManager();
2843 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
2844 widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
2845 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
2846 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
2847 Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
2848 return widgetsAndShortcuts;
2849 }
2850
2851 private static boolean isPackageDisabled(Context context, String packageName, UserHandleCompat user) 🔵
2852 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2853 return !launcherApps.isPackageEnabledForProfile(packageName, user);
2854 }
2855
2856 public static boolean isValidPackageActivity(Context context, ComponentName cn, UserHandleCompat user🔵
2857 if (cn == null) {
2858 return false;
2859 }
2860 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2861 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
2862 return false;
2863 }
2864 return launcherApps.isActivityEnabledForProfile(cn, user);
2865 }
2866
2867 public static boolean isValidPackage(Context context, String packageName, UserHandleCompat user) {
2868 if (packageName == null) {
2869 return false;
2870 }
2871 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2872 return launcherApps.isPackageEnabledForProfile(packageName, user);
2873 }
2874
2875 /**
2876 * Make an ShortcutInfo object for a restored application or shortcut item that points
2877 * to a package that is not yet installed on the system.
2878 */
2879 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent, int promiseType🔵
2880 final ShortcutInfo info = new ShortcutInfo();
2881 info.user = UserHandleCompat.myUserHandle();
2882 mIconCache.getTitleAndIcon(info, intent, info.user, true);
2883 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
2884 String title = (cursor != null) ? cursor.getString(titleIndex) : null;
2885 if (!TextUtils.isEmpty(title)) {
2886 info.title = title;
2887 }
2888 info.status = ShortcutInfo.FLAG_RESTORED_ICON;
2889 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
2890 if (TextUtils.isEmpty(info.title)) {
2891 info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
2892 }
2893 info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
2894 } else {
2895 throw new InvalidParameterException("Invalid restoreType " + promiseType);
2896 }
2897 info.contentDescription = mUserManager.getBadgedLabelForUser(info.title.toString(), info.user);
2898 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
2899 info.promisedIntent = intent;
2900 return info;
2901 }
2902
2903 /**
2904 * Make an Intent object for a restored application or shortcut item that points
2905 * to the market page for the item.
2906 */
2907 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
2908 ComponentName componentName = intent.getComponent();
2909 return getMarketIntent(componentName.getPackageName());
2910 }
2911
2912 static Intent getMarketIntent(String packageName) {
2913 return new Intent(Intent.ACTION_VIEW).setData(new Uri.Builder().scheme("market").authority("detai🔵
2914 }
2915
2916 /**
2917 * This is called from the code that adds shortcuts from the intent receiver. This
2918 * doesn't have a Cursor, but
2919 */
2920 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, UserHandleCompat user, Con🔵
2921 return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
2922 }
2923
2924 /**
2925 * Make an ShortcutInfo object for a shortcut that is an application.
2926 *
2927 * If c is not null, then it will be used to fill in missing data like the title and icon.
2928 */
2929 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, UserHandleCompat user, Con🔵
2930 if (user == null) {
2931 Log.d(TAG, "Null user found in getShortcutInfo");
2932 return null;
2933 }
2934 ComponentName componentName = intent.getComponent();
2935 if (componentName == null) {
2936 Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
2937 return null;
2938 }
2939 Intent newIntent = new Intent(intent.getAction(), null);
2940 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2941 newIntent.setComponent(componentName);
2942 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
2943 if ((lai == null) && (!allowMissingTarget)) {
2944 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
2945 return null;
2946 }
2947 final ShortcutInfo info = new ShortcutInfo();
2948 // the resource -- This may implicitly give us back the fallback icon,
2949 // but don't worry about that. All we're doing with usingFallbackIcon is
2950 // to avoid saving lots of copies of that in the database, and most apps
2951 // have icons anyway.
2952 Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
2953 // the db
2954 if (icon == null) {
2955 if (c != null) {
2956 icon = getIconFromCursor(c, iconIndex, context);
2957 }
2958 }
2959 // the fallback icon
2960 if (icon == null) {
2961 icon = mIconCache.getDefaultIcon(user);
2962 info.usingFallbackIcon = true;
2963 }
2964 info.setIcon(icon);
2965 // From the cache.
2966 if (labelCache != null) {
2967 info.title = labelCache.get(componentName);
2968 }
2969 // from the resource
2970 if ((info.title == null) && (lai != null)) {
2971 info.title = lai.getLabel();
2972 if (labelCache != null) {
2973 labelCache.put(componentName, info.title);
2974 }
2975 }
2976 // from the db
2977 if (info.title == null) {
2978 if (c != null) {
2979 info.title = c.getString(titleIndex);
2980 }
2981 }
2982 // fall back to the class name of the activity
2983 if (info.title == null) {
2984 info.title = componentName.getClassName();
2985 }
2986 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
2987 info.user = user;
2988 info.contentDescription = mUserManager.getBadgedLabelForUser(info.title.toString(), info.user);
2989 return info;
2990 }
2991
2992 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos, ItemInfoFilter f) {
2993 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
2994 for (ItemInfo i : infos) {
2995 if (i instanceof ShortcutInfo) {
2996 ShortcutInfo info = ((ShortcutInfo) (i));
2997 ComponentName cn = info.getTargetComponent();
2998 if ((cn != null) && f.filterItem(null, info, cn)) {
2999 filtered.add(info);
3000 }
3001 } else if (i instanceof FolderInfo) {
3002 FolderInfo info = ((FolderInfo) (i));
3003 for (ShortcutInfo s : info.contents) {
3004 ComponentName cn = s.getTargetComponent();
3005 if ((cn != null) && f.filterItem(info, s, cn)) {
3006 filtered.add(s);
3007 }
3008 }
3009 } else if (i instanceof LauncherAppWidgetInfo) {
3010 LauncherAppWidgetInfo info = ((LauncherAppWidgetInfo) (i));
3011 ComponentName cn = info.providerName;
3012 if ((cn != null) && f.filterItem(null, info, cn)) {
3013 filtered.add(info);
3014 }
3015 }
3016 }
3017 return new ArrayList<ItemInfo>(filtered);
3018 }
3019
3020 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname, final UserHandleCo🔵
3021 ItemInfoFilter filter = new ItemInfoFilter() {
3022 @Override
3023 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
3024 if (info.user == null) {
3025 return cn.equals(cname);
3026 } else {
3027 return cn.equals(cname) && info.user.equals(user);
3028 }
3029 }
3030 };
3031 return filterItemInfos(sBgItemsIdMap.values(), filter);
3032 }
3033
3034 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
3035 if (i instanceof ShortcutInfo) {
3036 ShortcutInfo info = ((ShortcutInfo) (i));
3037 // We need to check for ACTION_MAIN otherwise getComponent() might
3038 // return null for some shortcuts (for instance, for shortcuts to
3039 // web pages.)
3040 Intent intent = info.intent;
3041 ComponentName name = intent.getComponent();
3042 if (((info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) && Intent.ACTION_MAI🔵
3043 return true;
3044 }
3045 // placeholder shortcuts get special treatment, let them through too.
3046 if (info.isPromise()) {
3047 return true;
3048 }
3049 }
3050 return false;
3051 }
3052
3053 /**
3054 * Make an ShortcutInfo object for a shortcut that isn't an application.
3055 */
3056 private ShortcutInfo getShortcutInfo(Cursor c, Context context, int iconTypeIndex, int iconPackageInd🔵
3057 Bitmap icon = null;
3058 final ShortcutInfo info = new ShortcutInfo();
3059 // Non-app shortcuts are only supported for current user.
3060 info.user = UserHandleCompat.myUserHandle();
3061 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3062 // TODO: If there's an explicit component and we can't install that, delete it.
3063 info.title = c.getString(titleIndex);
3064 int iconType = c.getInt(iconTypeIndex);
3065 switch (iconType) {
3066 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE :
3067 String packageName = c.getString(iconPackageIndex);
3068 String resourceName = c.getString(iconResourceIndex);
3069 PackageManager packageManager = context.getPackageManager();
3070 info.customIcon = false;
3071 // the resource
3072 try {
3073 Resources resources = packageManager.getResourcesForApplication(packageName);
3074 if (resources != null) {
3075 final int id = resources.getIdentifier(resourceName, null, null);
3076 icon = Utilities.createIconBitmap(mIconCache.getFullResIcon(resources, id), conte🔵
3077 }
3078 } catch (java.lang.Exception e) {
3079 // drop this. we have other places to look for icons
3080 }
3081 // the db
3082 if (icon == null) {
3083 icon = getIconFromCursor(c, iconIndex, context);
3084 }
3085 // the fallback icon
3086 if (icon == null) {
3087 icon = mIconCache.getDefaultIcon(info.user);
3088 info.usingFallbackIcon = true;
3089 }
3090 break;
3091 case LauncherSettings.Favorites.ICON_TYPE_BITMAP :
3092 icon = getIconFromCursor(c, iconIndex, context);
3093 if (icon == null) {
3094 icon = mIconCache.getDefaultIcon(info.user);
3095 info.customIcon = false;
3096 info.usingFallbackIcon = true;
3097 } else {
3098 info.customIcon = true;
3099 }
3100 break;
3101 default :
3102 icon = mIconCache.getDefaultIcon(info.user);
3103 info.usingFallbackIcon = true;
3104 info.customIcon = false;
3105 break;
3106 }
3107 info.setIcon(icon);
3108 return info;
3109 }
3110
3111 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
3112 // suppress dead code warning
3113 @SuppressWarnings("all")
3114 final boolean debug = false;
3115 if (debug) {
3116 Log.d(TAG, "getIconFromCursor app=" + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Fa🔵
3117 }
3118 byte[] data = c.getBlob(iconIndex);
3119 try {
3120 return Utilities.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), contex🔵
3121 } catch (java.lang.Exception e) {
3122 return null;
3123 }
3124 }
3125
3126 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen, int cellX, int cel🔵
3127 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
3128 if (info == null) {
3129 return null;
3130 }
3131 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
3132 return info;
3133 }
3134
3135 /**
3136 * Attempts to find an AppWidgetProviderInfo that matches the given component.
3137 */
3138 static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context, ComponentName co🔵
3139 List<AppWidgetProviderInfo> widgets = AppWidgetManager.getInstance(context).getInstalledProviders🔵
3140 for (AppWidgetProviderInfo info : widgets) {
3141 if (info.provider.equals(component)) {
3142 return info;
3143 }
3144 }
3145 return null;
3146 }
3147
3148 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
3149 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3150 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3151 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3152 if (intent == null) {
3153 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3154 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3155 return null;
3156 }
3157 Bitmap icon = null;
3158 boolean customIcon = false;
3159 ShortcutIconResource iconResource = null;
3160 if ((bitmap != null) && (bitmap instanceof Bitmap)) {
3161 icon = Utilities.createIconBitmap(new FastBitmapDrawable(((Bitmap) (bitmap))), context);
3162 customIcon = true;
3163 } else {
3164 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
3165 if ((extra != null) && (extra instanceof ShortcutIconResource)) {
3166 try {
3167 iconResource = ((ShortcutIconResource) (extra));
3168 final PackageManager packageManager = context.getPackageManager();
3169 Resources resources = packageManager.getResourcesForApplication(iconResource.packageN🔵
3170 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
3171 icon = Utilities.createIconBitmap(mIconCache.getFullResIcon(resources, id), context);
3172 } catch (java.lang.Exception e) {
3173 Log.w(TAG, "Could not load shortcut icon: " + extra);
3174 }
3175 }
3176 }
3177 final ShortcutInfo info = new ShortcutInfo();
3178 // Only support intents for current user for now. Intents sent from other
3179 // users wouldn't get here without intent forwarding anyway.
3180 info.user = UserHandleCompat.myUserHandle();
3181 if (icon == null) {
3182 if (fallbackIcon != null) {
3183 icon = fallbackIcon;
3184 } else {
3185 icon = mIconCache.getDefaultIcon(info.user);
3186 info.usingFallbackIcon = true;
3187 }
3188 }
3189 info.setIcon(icon);
3190 info.title = name;
3191 info.contentDescription = mUserManager.getBadgedLabelForUser(info.title.toString(), info.user);
3192 info.intent = intent;
3193 info.customIcon = customIcon;
3194 info.iconResource = iconResource;
3195 return info;
3196 }
3197
3198 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c, int iconInde🔵
3199 // If apps can't be on SD, don't even bother.
3200 if (!mAppsCanBeOnRemoveableStorage) {
3201 return false;
3202 }
3203 // If this icon doesn't have a custom icon, check to see
3204 // what's stored in the DB, and if it doesn't match what
3205 // we're going to show, store what we are going to show back
3206 // into the DB. We do this so when we're loading, if the
3207 // package manager can't find an icon (for example because
3208 // the app is on SD) then we can use that instead.
3209 if ((!info.customIcon) && (!info.usingFallbackIcon)) {
3210 cache.put(info, c.getBlob(iconIndex));
3211 return true;
3212 }
3213 return false;
3214 }
3215
3216 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
3217 boolean needSave = false;
3218 try {
3219 if (data != null) {
3220 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
3221 Bitmap loaded = info.getIcon(mIconCache);
3222 needSave = !saved.sameAs(loaded);
3223 } else {
3224 needSave = true;
3225 }
3226 } catch (java.lang.Exception e) {
3227 needSave = true;
3228 }
3229 if (needSave) {
3230 Log.d(TAG, "going to save icon bitmap for info=" + info);
3231 // This is slower than is ideal, but this only happens once
3232 // or when the app is updated with a new icon.
3233 updateItemInDatabase(context, info);
3234 }
3235 }
3236
3237 /**
3238 * Return an existing FolderInfo object if we have encountered this ID previously,
3239 * or make a new one.
3240 */
3241 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
3242 // See if a placeholder was created for us already
3243 FolderInfo folderInfo = folders.get(id);
3244 if (folderInfo == null) {
3245 // No placeholder -- create a new instance
3246 folderInfo = new FolderInfo();
3247 folders.put(id, folderInfo);
3248 }
3249 return folderInfo;
3250 }
3251
3252 public static final Comparator<AppInfo> getAppNameComparator() {
3253 final Collator collator = Collator.getInstance();
3254 return new Comparator<AppInfo>() {
3255 public final int compare(AppInfo a, AppInfo b) {
3256 if (a.user.equals(b.user)) {
3257 int result = collator.compare(a.title.toString().trim(), b.title.toString().trim());
3258 if (result == 0) {
3259 result = a.componentName.compareTo(b.componentName);
3260 }
3261 return result;
3262 } else {
3263 // TODO Need to figure out rules for sorting
3264 // profiles, this puts work second.
3265 return a.user.toString().compareTo(b.user.toString());
3266 }
3267 }
3268 };
3269 }
3270
3271 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR = new Comparator<AppInfo>() {
3272 public final int compare(AppInfo a, AppInfo b) {
3273 if (a.firstInstallTime < b.firstInstallTime) {
3274 return 1;
3275 }
3276 if (a.firstInstallTime > b.firstInstallTime) {
3277 return -1;
3278 }
3279 return 0;
3280 }
3281 };
3282
3283 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
3284 if (info.activityInfo != null) {
3285 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
3286 } else {
3287 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
3288 }
3289 }
3290
3291 public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
3292 private Collator mCollator;
3293
3294 private HashMap<Object, CharSequence> mLabelCache;
3295
3296 ShortcutNameComparator(PackageManager pm) {
3297 mLabelCache = new HashMap<Object, CharSequence>();
3298 mCollator = Collator.getInstance();
3299 }
3300
3301 ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
3302 mLabelCache = labelCache;
3303 mCollator = Collator.getInstance();
3304 }
3305
3306 public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
3307 String labelA;
3308 String labelB;
3309 ComponentName keyA = a.getComponentName();
3310 ComponentName keyB = b.getComponentName();
3311 if (mLabelCache.containsKey(keyA)) {
3312 labelA = mLabelCache.get(keyA).toString();
3313 } else {
3314 labelA = a.getLabel().toString().trim();
3315 mLabelCache.put(keyA, labelA);
3316 }
3317 if (mLabelCache.containsKey(keyB)) {
3318 labelB = mLabelCache.get(keyB).toString();
3319 } else {
3320 labelB = b.getLabel().toString().trim();
3321 mLabelCache.put(keyB, labelB);
3322 }
3323 return mCollator.compare(labelA, labelB);
3324 }
3325 }
3326
3327 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
3328 private final AppWidgetManagerCompat mManager;
3329
3330 private final PackageManager mPackageManager;
3331
3332 private final HashMap<Object, String> mLabelCache;
3333
3334 private final Collator mCollator;
3335
3336 WidgetAndShortcutNameComparator(Context context) {
3337 mManager = AppWidgetManagerCompat.getInstance(context);
3338 mPackageManager = context.getPackageManager();
3339 mLabelCache = new HashMap<Object, String>();
3340 mCollator = Collator.getInstance();
3341 }
3342
3343 public final int compare(Object a, Object b) {
3344 String labelA;
3345 String labelB;
3346 if (mLabelCache.containsKey(a)) {
3347 labelA = mLabelCache.get(a);
3348 } else {
3349 labelA = (a instanceof AppWidgetProviderInfo) ? mManager.loadLabel(((AppWidgetProviderInf🔵
3350 mLabelCache.put(a, labelA);
3351 }
3352 if (mLabelCache.containsKey(b)) {
3353 labelB = mLabelCache.get(b);
3354 } else {
3355 labelB = (b instanceof AppWidgetProviderInfo) ? mManager.loadLabel(((AppWidgetProviderInf🔵
3356 mLabelCache.put(b, labelB);
3357 }
3358 return mCollator.compare(labelA, labelB);
3359 }
3360 }
3361
3362 static boolean isValidProvider(AppWidgetProviderInfo provider) {
3363 return ((provider != null) && (provider.provider != null)) && (provider.provider.getPackageName()🔵
3364 }
3365
3366 public void dumpState() {
3367 Log.d(TAG, "mCallbacks=" + mCallbacks);
3368 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3369 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3370 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3371 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
3372 if (mLoaderTask != null) {
3373 mLoaderTask.dumpState();
3374 } else {
3375 Log.d(TAG, "mLoaderTask=null");
3376 }
3377 }
3378 }
|